Commit 4f7a21d8 authored by Kriengkrai Yothee's avatar Kriengkrai Yothee

เกือบเสร็จ

parent bc3ffdaa
Pipeline #1067 failed with stages
# communutyTOPIC
# topic1
for select topic1
.git/
dist/
examples/
node_modules/
# 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
NODE_ENV=development
SERVER_PORT=4040
JWT_SECRET=0a6b944d-d2fb-46fc-a85e-0295c986cd9f
MONGO_HOST=mongodb://localhost/odmp
MEAN_FRONTEND=angular
# 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
/.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
FROM node:lts-buster
WORKDIR /usr/src/app
ADD . /usr/src/app
RUN yarn
RUN yarn build
EXPOSE 4040
CMD ["yarn", "serve"]
## 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
```
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 was done by Amos Haviv and sponsered by Linnovate.
- Inspired by the great work of Madhusudhan Srinivasa.
# การเพิ่ม model ชื่อ Student ใน project
## 1. เพิ่มไฟล์ `server/models/student.model.js`
```js
const mongoose = require('mongoose');
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/guide.html
*/
const StudentSchema = new mongoose.Schema(
{
sid: { type: String, required: true },
first: { type: String, required: true },
last: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
},
{
versionKey: false
}
);
module.exports = mongoose.model('Student', StudentSchema);
```
## 2. เพิ่มไฟล์ `server/controllers/student.controller.js`
```js
const Joi = require('joi');
const Student = require('../models/student.model');
const studentSchema = Joi.object({
sid: Joi.number().integer().required(),
first: Joi.string().required(),
last: Joi.string().required()
})
module.exports = {
insert,
get,
getAll,
search,
}
async function insert(student) {
student = await Joi.validate(student, studentSchema, { abortEarly: false });
return await new Student(student).save();
}
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/api.html
*/
async function get(sid) {
return await Student.find({sid: sid});
}
async function getAll() {
return await Student.find();
}
async function search(key, value) {
let query = {};
query[key] = value;
return await Student.find(query);
}
```
## 3. เพิ่มไฟล์ `server/routes/student.route.js`
```js
const express = require('express');
const asyncHandler = require('express-async-handler');
const studentCtrl = require('../controllers/student.controller');
const router = express.Router();
module.exports = router;
//router.use(passport.authenticate('jwt', { session: false }))
router.route('/').post(asyncHandler(insert));
router.route('/get/:sid(\d+)').get(asyncHandler(get));
router.route('/all').get(asyncHandler(getAll));
router.route('/search').get(asyncHandler(search));
async function insert(req, res) {
let student = await studentCtrl.insert(req.body);
res.json(student);
}
async function get(req, res) {
let all_students = await studentCtrl.get(req.params['sid']);
res.json(all_students);
}
async function getAll(req, res) {
let all_students = await studentCtrl.getAll();
res.json(all_students);
}
async function search(req, res) {
let result = await studentCtrl.search(req.params['key'], req.params['value']);
res.json(result);
}
```
## 4. เพิ่มเส้นทางการเรียกในไฟล์ `server/routes/index.route.js`
```js
const express = require('express');
const userRoutes = require('./user.route');
const studentRoutes = require('./student.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);
router.use('/student', studentRoutes);
module.exports = router;
```
## 5. ตัวอย่างสคริปเพื่อกำหนดข้อมูลเบื้องต้น `scripts/init-students.js`
```js
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');
const config = require('../server/config/config');
const Student = require('../server/models/student.model');
// 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}`);
});
const students = [
{ sid: 60112233440, first: 'ชูใจ', last: 'เลิศล้ำ' },
{ sid: 60112233441, first: 'มานี', last: 'รักเผ่าไทย' },
{ sid: 60112233442, first: 'ปิติ', last: 'พิทักษ์ถิ่น' },
{ sid: 60112233443, first: 'มานะ', last: 'รักเผ่าไทย' },
{ sid: 60112233444, first: 'วีระ', last: 'ประสงค์สุข' }
];
Student.insertMany(students, (error, docs) => {
if (error) {
console.error(error);
} else {
console.log(docs);
}
mongoose.connection.close();
});
```
## 6. เรียกใช้ `scripts/init-students.js`
```sh
node scripts/init-students.js
```
## 7. เรียกดูข้อมูลได้จาก `http://localhost:4000/api/student/all`
theme: jekyll-theme-minimal
logo: https://www.linnovate.net/sites/all/themes/linnovate/images/mean-picture.png
<!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>
{
"$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": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"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-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"./tsconfig.app.json",
"./tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "mean",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"styleext": "scss"
},
"@schematics/angular:directive": {
"prefix": "app"
}
},
"cli": {
"analytics": "c4b1fc92-ebd6-4fa9-a8a1-067afbef23d9"
}
}
\ No newline at end of file
---
---
@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;
}
}
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11
version: '3'
services:
app:
build: ./
image: mean
container_name: mean
ports:
- 80:4040
expose:
- 4040
environment:
NODE_ENV: production
SERVER_PORT: 4040
JWT_SECRET: 0a6b944d-d2fb-46fc-a85e-0295c986cd9f
MONGO_HOST: mongodb://mongo/odmp
restart: always
depends_on:
- mongo
meanexpress:
container_name: meanexpress
image: mongo-express
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_AUTH_DATABASE: odmp
#ME_CONFIG_MONGODB_AUTH_USERNAME: admin
#ME_CONFIG_MONGODB_AUTH_PASSWORD: pass
restart: always
depends_on:
- mongo
mongo:
container_name: meanmongo
image: mongo:3.6
ports:
- 27017:27017
expose:
- 27017
// 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
});
};
{
"name": "mean",
"version": "2.0.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"yarn": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.10.tgz",
"integrity": "sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA=="
}
}
}
{
"name": "mean",
"version": "2.0.2",
"license": "MIT",
"scripts": {
"ng": "ng",
"serve": "node server",
"import student": "node scripts/init-students.js",
"start": "concurrently -c \"yellow.bold,green.bold\" -n \"SERVER,BUILD\" \"nodemon server\" \"ng build --watch\"",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
},
"private": true,
"dependencies": {
"@angular/animations": "^9.1.4",
"@angular/cdk": "^9.2.1",
"@angular/common": "^9.1.4",
"@angular/compiler": "^9.1.4",
"@angular/core": "^9.1.4",
"@angular/forms": "^9.1.4",
"@angular/material": "^9.2.1",
"@angular/platform-browser": "^9.1.4",
"@angular/platform-browser-dynamic": "^9.1.4",
"@angular/router": "^9.1.4",
"bcrypt": "^3.0.2",
"body-parser": "^1.18.2",
"compression": "^1.7.2",
"cookie-parser": "^1.4.3",
"cors": "^2.8.4",
"dotenv": "^6.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": "^1.2.1",
"helmet": "^3.21.1",
"http-errors": "^1.6.3",
"joi": "^13.3.0",
"jsonwebtoken": "^8.2.1",
"method-override": "^2.3.10",
"mongoose": "^5.7.5",
"morgan": "^1.9.1",
"nodemon": "^1.17.5",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"rxjs": "^6.5.5",
"swagger-ui-express": "^3.0.9",
"yarn": "^1.22.10",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.901.4",
"@angular/cli": "^9.1.4",
"@angular/compiler-cli": "^9.1.4",
"@angular/language-service": "^9.1.4",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~12.12.14",
"codelyzer": "^5.2.0",
"concurrently": "^3.5.1",
"jasmine-core": "~3.1.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^3.1.3",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "2.0.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "1.1.0",
"ts-node": "~6.1.0",
"tslint": "^5.20.1",
"typescript": "3.8.3"
}
}
// 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 } }));
}
};
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');
const config = require('../server/config/config');
const Student = require('../server/models/student.model');
// 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}`);
});
const students = [
{ sid: 60112233440, first: 'ชูใจ', last: 'เลิศล้ำ' },
{ sid: 60112233441, first: 'มานี', last: 'รักเผ่าไทย' },
{ sid: 60112233442, first: 'ปิติ', last: 'พิทักษ์ถิ่น' },
{ sid: 60112233443, first: 'มานะ', last: 'รักเผ่าไทย' },
{ sid: 60112233444, first: 'วีระ', last: 'ประสงค์สุข' }
];
Student.insertMany(students, (error, docs) => {
if (error) {
console.error(error);
} else {
console.log(docs);
}
mongoose.connection.close();
});
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 } = Joi.validate(process.env, envVarsSchema);
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;
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;
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);
});
}
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;
{
"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"
}
}]
}
}
}
\ No newline at end of file
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);
}
const Joi = require("joi");
const Review = require("../models/review.model");
const reviewSchema = Joi.object({
// sid: Joi.number().integer().required(),
user_id: Joi.string().required(),
catagory: Joi.string().required(),
namemovie: Joi.string().required(),
title: Joi.string().required(),
description: Joi.string().required(),
});
module.exports = {
insert,
get,
getAll,
search,
deleteData,
updateData,
};
async function insert(review) {
review = await Joi.validate(review, reviewSchema, { abortEarly: false });
return await new Review(review).save();
}
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/api.html
*/
async function get(_id) {
return await Review.find({ _id: _id });
}
async function getAll() {
return await Review.find();
}
async function search(key, value) {
let query = {};
query[key] = value;
return await Review.find(query);
}
async function deleteData(_id) {
return await Review.findByIdAndDelete(_id);
}
async function updateData(_id,data) {
return Review.findByIdAndUpdate(_id, data);
}
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 Joi.validate(user, userSchema, { abortEarly: false });
user.hashedPassword = bcrypt.hashSync(user.password, 10);
delete user.password;
return await new User(user).save();
}
// 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;
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;
const mongoose = require("mongoose");
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/guide.html
*/
const ReviewSchema = new mongoose.Schema(
{
user_id: { type: String, required: true },
catagory: { type: String, required: true },
namemovie: { type: String, required: true },
title: { type: String, required: true },
description: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
},
{
versionKey: false,
}
);
module.exports = mongoose.model("Review", ReviewSchema);
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);
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 });
}
const express = require('express');
const userRoutes = require('./user.route');
const reviewRoutes = require('./review.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);
router.use('/review', reviewRoutes);
module.exports = router;
const express = require('express');
const asyncHandler = require('express-async-handler');
const reviewCtrl = require('../controllers/review.controller');
const router = express.Router();
module.exports = router;
//router.use(passport.authenticate('jwt', { session: false }))
router.route('/').post(asyncHandler(insert));
router.route('/get/:_id').get(asyncHandler(get));
router.route('/all').get(asyncHandler(getAll));
router.route('/search').get(asyncHandler(search));
router.route('/delete/:_id').delete(asyncHandler(deleteData));
router.route('/update/:_id').put(asyncHandler(updateData));
async function insert(req, res) {
let review = await reviewCtrl.insert(req.body);
res.json(review);
}
async function get(req, res) {
let all_reviews = await reviewCtrl.get(req.params['_id']);
res.json(all_reviews);
}
async function getAll(req, res) {
let all_reviews = await reviewCtrl.getAll();
res.json(all_reviews);
}
async function search(req, res) {
let result = await reviewCtrl.search(req.params['key'], req.params['value']);
res.json(result);
}
async function deleteData(req, res) {
let all_reviews = await reviewCtrl.deleteData(req.params['_id']);
res.json(all_reviews);
}
async function updateData(req, res) {
let all_reviews = await reviewCtrl.updateData(req.params['_id'],req.body);
res.json(all_reviews);
}
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);
}
/*-----------------------------------------------
Variables
-----------------------------------------------*/
$linesColor: #dbdbdb;
$categoryTitleColor: #686868;
$categoryEntityColor: #3F3F3F;
\ No newline at end of file
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 {}
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));
}
}
<h4>HELLO FROM ADMIN PAGE</h4>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
})
export class AdminComponent implements OnInit {
constructor() {}
public ngOnInit() {
}
}
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 {}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './shared/guards';
import { HomeComponent } from './home/home.component';
import { HeroComponent } from './hero/hero.component';
import { CreateComponent } from './create/create.component';
import { ViewdataComponent } from './viewdata/viewdata.component';
import { EditComponent } from './edit/edit.component';
const routes: Routes = [
{
path: '',
component: HomeComponent,
canActivate: [AuthGuard],
},
{
path: 'create',
component: CreateComponent,
canActivate: [AuthGuard],
},
{
path: 'viewdata/:dataId',
component: ViewdataComponent,
canActivate: [AuthGuard],
},
{
path: 'edit/:dataId',
component: EditComponent,
canActivate: [AuthGuard],
},
{
path: 'hero',
component: HeroComponent,
},
{
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 {}
<app-header [user]="user$ | async"></app-header>
<div class="wrapper-app">
<router-outlet></router-outlet>
</div>
<footer></footer>
.wrapper-app {
}
\ No newline at end of file
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`)
);
});
}
}
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';
import { HeroComponent } from './hero/hero.component';
import { CreateComponent } from './create/create.component';
import { ViewdataComponent } from './viewdata/viewdata.component';
import { EditComponent } from './edit/edit.component';
export function appInitializerFactory(authService: AuthService) {
return () => authService.checkTheUserOnTheFirstLoad();
}
@NgModule({
imports: [BrowserAnimationsModule, HttpClientModule, SharedModule, AppRoutingModule],
declarations: [AppComponent, HeaderComponent, HomeComponent, HeroComponent, CreateComponent, ViewdataComponent, EditComponent],
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 {}
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);
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 400px;
margin: 10% auto;
}
.mat-card-title {
font-size: 16px;
}
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 {}
<main id="tt-pageContent" class="tt-offset-none">
<div class="container">
<div class="tt-loginpages-wrapper">
<div class="tt-loginpages">
<a href="/" class="tt-block-title">
<img src="../../assets/images/logo.png" alt="">
<div class="tt-title">
Welcome to Topic
</div>
<div class="tt-description">
Log into your account to unlock true power of community.
</div>
</a>
<form class="form-default">
<div class="form-group">
<label for="loginUserName">Email*</label>
<input [(ngModel)]="email" name="email" required type="text" class="form-control" id="loginEmail"
placeholder="">
</div>
<div class="form-group">
<label for="loginUserPassword">Password*</label>
<input [(ngModel)]="password" type="password" required name="password" class="form-control"
id="loginUserPassword" placeholder="">
</div>
<div class="row">
<div class="col">
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox01" name="checkbox">
<label for="settingsCheckBox01">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">Remember me</span>
</label>
</div>
</div>
</div>
<div class="col ml-auto text-right">
<a href="#" class="tt-underline">Forgot Password</a>
</div>
</div>
<div class="form-group">
<a (click)="login()" class="btn btn-primary btn-block">Log in</a>
</div>
<span>Don't have an account ? <a [routerLink]="['/auth/register']">Signup here</a> here</span>
<div class="tt-notes">
By Logging in, signing in or continuing, I agree to
Topic’s <a routerLink="/about" class="tt-underline">Terms of Use</a> and <a routerLink="/about" class="tt-underline">Privacy
Policy.</a>
</div>
</form>
</div>
</div>
</div>
</main>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
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('/');
});
}
}
<!-- tt-mobile menu -->
<nav class="panel-menu" id="mobile-menu">
<ul>
</ul>
<div class="mm-navbtn-names">
<div class="mm-closebtn">
Close
<div class="tt-icon">
<svg>
<use xlink:href="#icon-cancel"></use>
</svg>
</div>
</div>
<div class="mm-backbtn">Back</div>
</div>
</nav>
<main id="tt-pageContent" class="tt-offset-none">
<div class="container">
<div class="tt-loginpages-wrapper">
<div class="tt-loginpages">
<a routerLink="/" class="tt-block-title">
<img src="../../assets/images/logo.png" alt="">
<div class="tt-title">
Welcome to Topic
</div>
<div class="tt-description">
Join the forum to unlock true power of community.
</div>
</a>
<form class="form-default" [formGroup]="userForm">
<div class="form-group">
<label for="loginUserName">FullName*</label>
<input formControlName="fullname" name="fullname" required type="text" class="form-control" id="FullName"
placeholder="">
</div>
<div class="form-group">
<label for="loginUserEmail">Email*</label>
<input formControlName="email" name="email" required type="text" class="form-control" id="loginUserEmail"
placeholder="">
</div>
<div class="form-group">
<label for="loginUserPassword">Password</label>
<input formControlName="password" type="password" name="password" required type="password"
class="form-control" id="loginUserPassword" placeholder="">
</div>
<div class="form-group">
<label for="loginUserPassword">Repeat Password</label>
<input formControlName="repeatPassword" type="password" name="repeatPassword" required type="password"
required class="form-control" id="loginUserPassword" placeholder="">
<mat-error *ngIf="repeatPassword.invalid && repeatPassword.hasError('passwordMatch')">Password mismatch
</mat-error>
</div>
<div class="form-group">
<a (click)="register()" class="btn btn-secondary btn-block">Create my account</a>
</div>
<p>Already have an account? <a [routerLink]="['/auth/login']" class="tt-underline">Login here</a></p>
<div class="tt-notes">
By signing up, signing in or continuing, I agree to
Topic’s <a [routerLink]="['/auth/login']" class="tt-underline">Terms of Use</a> and <a [routerLink]="['/auth/login']" class="tt-underline">Privacy
Policy.</a>
</div>
</form>
</div>
</div>
</div>
</main>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RegisterComponent } from './register.component';
describe('RegisterComponent', () => {
let component: RegisterComponent;
let fixture: ComponentFixture<RegisterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RegisterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RegisterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import {
FormGroup,
FormControl,
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: FormControl): ValidationErrors | null {
const password = control.root.get('password');
return password && control.value !== password.value
? {
passwordMatch: true,
}
: null;
}
userForm = new FormGroup({
fullname: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required]),
repeatPassword: new FormControl('', [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(data => {
this.router.navigate(['']);
});
}
}
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 800px;
margin: auto;
margin-top: 4%;
}
\ No newline at end of file
<main id="tt-pageContent">
<div class="container">
<div class="tt-wrapper-inner">
<h1 class="tt-title-border">
Create New Topic
</h1>
<div class="tt-topic-list">
<div class="tt-list-header">
<div class="tt-col-topic">Topic</div>
<div class="tt-col-category">Category</div>
</div>
<div class="tt-item tt-itemselect">
<div class="tt-col-avatar">
<svg class="tt-icon">
<use xlink:href="#icon-heading"></use>
</svg>
</div>
<div class="tt-col-description">
<h6 class="tt-title"><a [routerLink]="['/']">
<svg class="tt-icon">
<use xlink:href="#icon-pinned"></use>
</svg>
<strong> {{ reviewAdd.namemovie}}</strong>
</a></h6>
<div class="row align-items-center no-gutters">
<div class="col-11">
<ul class="tt-list-badge">
<li class="show-mobile"><a href="#"><span
class="tt-color01 tt-badge">politics</span></a></li>
<li><a href="#"><span class="tt-badge">#{{reviewAdd.description}}</span></a></li>
</ul>
</div>
</div>
</div>
<div class="tt-col-category"><span class="tt-color01 tt-badge">{{ reviewAdd.catagory }}</span></div>
</div>
<div class="tt-item">
<div class="tt-col-avatar">
<svg class="tt-icon">
<use xlink:href="#icon-performatted"></use>
</svg>
</div>
<div class="tt-col-description">
<h6 class="tt-title"><a >
{{ reviewAdd.title}}
</a></h6>
<div class="row align-items-center no-gutters hide-desktope">
<div class="col-11">
<ul class="tt-list-badge">
<li class="show-mobile"><a href="#"><span class="tt-color05 tt-badge">music</span></a>
</li>
</ul>
</div>
<div class="col-1 ml-auto show-mobile">
<div class="tt-value">1d</div>
</div>
</div>
</div>
</div>
</div>
<br><br>
<form class="form-default form-create-topic">
<div class="form-group">
<label for="inputTopicTitle">Topic Title</label>
<div class="tt-value-wrapper">
<input type="text" matInput
placeholder="Placeholder"
name="namemovie"
[(ngModel)]="reviewAdd.namemovie" class="form-control" id="inputTopicTitle" placeholder="Subject of your topic">
<span class="tt-value-input">99</span>
</div>
<div class="tt-note">Describe your topic well, while keeping the subject as short as possible.</div>
</div>
<div class="pt-editor">
<h6 class="pt-title">Topic Body</h6>
<div class="pt-row">
<div class="col-left">
</div>
</div>
<div class="form-group">
<textarea matInput
placeholder="Placeholder"
name="title"
[(ngModel)]="reviewAdd.title" class="form-control" rows="5" placeholder="Lets get started"></textarea>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="inputTopicTitle">Category</label>
<select class="form-control" [(ngModel)]="reviewAdd.catagory" name="catagory">
<option value="option"
*ngFor="let catagory of catagorys"
[value]="catagory.type"
>{{ catagory.type }}</option>
</select>
</div>
</div>
<div class="col-md-8">
<div class="form-group">
<label for="inputTopicTags">Tags</label>
<input type="text" name="description" [(ngModel)]="reviewAdd.description" class="form-control" id="inputTopicTags" placeholder=" #tags">
</div>
</div>
</div>
<div class="row">
<div class="col-auto ml-md-auto">
<a (click)="submitReviewAdd()"
color="primary"
*ngIf="
reviewAdd.catagory &&
reviewAdd.namemovie &&
reviewAdd.title &&
reviewAdd.description
" class="btn btn-secondary btn-width-lg">Create Post</a>
</div>
</div>
</div>
</form>
</div>
<div class="tt-topic-list tt-offset-top-30"></div>
</div>
</main>
<div id="js-popup-settings" class="tt-popup-settings">
<div class="tt-btn-col-close">
<a href="#">
<span class="tt-icon-title">
<svg>
<use xlink:href="#icon-settings_fill"></use>
</svg>
</span>
<span class="tt-icon-text">
Settings
</span>
<span class="tt-icon-close">
<svg>
<use xlink:href="#icon-cancel"></use>
</svg>
</span>
</a>
</div>
<form class="form-default">
<div class="tt-form-upload">
<div class="row no-gutter">
<div class="col-auto">
<div class="tt-avatar">
<svg>
<use xlink:href="#icon-ava-d"></use>
</svg>
</div>
</div>
<div class="col-auto ml-auto">
<a href="#" class="btn btn-primary">Upload Picture</a>
</div>
</div>
</div>
<div class="form-group">
<label for="settingsUserName">Username</label>
<input type="text" name="name" class="form-control" id="settingsUserName" placeholder="azyrusmax">
</div>
<div class="form-group">
<label for="settingsUserEmail">Email</label>
<input type="text" name="name" class="form-control" id="settingsUserEmail" placeholder="Sample@sample.com">
</div>
<div class="form-group">
<label for="settingsUserPassword">Password</label>
<input type="password" name="name" class="form-control" id="settingsUserPassword" placeholder="************">
</div>
<div class="form-group">
<label for="settingsUserLocation">Location</label>
<input type="text" name="name" class="form-control" id="settingsUserLocation" placeholder="Slovakia">
</div>
<div class="form-group">
<label for="settingsUserWebsite">Website</label>
<input type="text" name="name" class="form-control" id="settingsUserWebsite" placeholder="Sample.com">
</div>
<div class="form-group">
<label for="settingsUserAbout">About</label>
<textarea name="" placeholder="Few words about you" class="form-control" id="settingsUserAbout"></textarea>
</div>
<div class="form-group">
<label for="settingsUserAbout">Notify me via Email</label>
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox01" name="checkbox">
<label for="settingsCheckBox01">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">When someone replies to my thread</span>
</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox02" name="checkbox">
<label for="settingsCheckBox02">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">When someone likes my thread or reply</span>
</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox03" name="checkbox">
<label for="settingsCheckBox03">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">When someone mentions me</span>
</label>
</div>
</div>
<div class="form-group">
<a href="#" class="btn btn-secondary">Save</a>
</div>
</form>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateComponent } from './create.component';
describe('CreateComponent', () => {
let component: CreateComponent;
let fixture: ComponentFixture<CreateComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CreateComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CreateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit , Input} from '@angular/core';
import { catagory } from '../../catagory';
import { CreateService } from '../service/create.service';
import { AuthService } from '@app/shared/services';
import { Router } from '@angular/router';
import { User } from '@app/shared/interfaces';
@Component({
selector: 'app-create',
templateUrl: './create.component.html',
styleUrls: ['./create.component.css']
})
export class CreateComponent implements OnInit {
@Input() user: User | null = null;
user_data: any;
reviewAdd = {
'user_id': '',
'catagory': '',
'namemovie': '',
'title': '',
'description': ''
};
catagorys = catagory;
constructor(private createService: CreateService, private authService: AuthService ,private router: Router,) { }
ngOnInit(): void {
this.authService.getUser().subscribe(data => this.user_data = data);
this.reviewAdd = {
'user_id': this.user_data._id,
'catagory': '',
'namemovie': '',
'title': '',
'description': ''
};
}
submitReviewAdd() {
this.createService.postReview(this.reviewAdd).subscribe((response: {}) =>
{alert('บันทึกเรียบร้อย'),
this.router.navigateByUrl('/');
}
);
console.warn(this.createService.postReview(this.reviewAdd));
console.warn(this.reviewAdd);
};
}
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 800px;
margin: auto;
margin-top: 4%;
}
<!-- ........................................ -->
<main id="tt-pageContent">
<div class="container">
<div class="tt-wrapper-inner">
<h1 class="tt-title-border">
Update Topic Post
</h1>
<div class="tt-topic-list">
<div class="tt-list-header">
<div class="tt-col-topic">Topic</div>
<div class="tt-col-category">Category</div>
</div>
<div class="tt-item tt-itemselect">
<div class="tt-col-avatar">
<svg class="tt-icon">
<use xlink:href="#icon-heading"></use>
</svg>
</div>
<div class="tt-col-description">
<h6 class="tt-title"><a [routerLink]="['/']">
<svg class="tt-icon">
<use xlink:href="#icon-pinned"></use>
</svg>
<strong> {{ data.namemovie}}</strong>
</a></h6>
<div class="row align-items-center no-gutters">
<div class="col-11">
<ul class="tt-list-badge">
<li class="show-mobile"><a href="#"><span
class="tt-color01 tt-badge">politics</span></a></li>
<li><a href="#"><span class="tt-badge">{{data.description}}</span></a></li>
</ul>
</div>
</div>
</div>
<div class="tt-col-category"><span class="tt-color01 tt-badge">{{ data.catagory }}</span></div>
</div>
<div class="tt-item">
<div class="tt-col-avatar">
<svg class="tt-icon">
<use xlink:href="#icon-performatted"></use>
</svg>
</div>
<div class="tt-col-description">
<h6 class="tt-title"><a >
{{ data.title}}
</a></h6>
<div class="row align-items-center no-gutters hide-desktope">
<div class="col-11">
<ul class="tt-list-badge">
<li class="show-mobile"><a href="#"><span class="tt-color05 tt-badge">music</span></a>
</li>
</ul>
</div>
<div class="col-1 ml-auto show-mobile">
<div class="tt-value">1d</div>
</div>
</div>
</div>
</div>
</div>
<br><br>
<form class="form-default form-create-topic">
<div class="form-group">
<label for="inputTopicTitle">Topic Title</label>
<div class="tt-value-wrapper">
<input type="text" matInput
placeholder="Placeholder"
name="namemovie"
[(ngModel)]="data.namemovie" class="form-control" id="inputTopicTitle" placeholder="Subject of your topic">
<span class="tt-value-input">99</span>
</div>
<div class="tt-note">Describe your topic well, while keeping the subject as short as possible.</div>
</div>
<div class="pt-editor">
<h6 class="pt-title">Topic Body</h6>
<div class="pt-row">
<div class="col-left">
</div>
</div>
<div class="form-group">
<textarea matInput
placeholder="Placeholder"
name="title"
[(ngModel)]="data.title" class="form-control" rows="5" placeholder="Lets get started"></textarea>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="inputTopicTitle">Category</label>
<select class="form-control" [(ngModel)]="data.catagory" name="catagory">
<option value="option"
*ngFor="let catagory of catagorys"
[value]="catagory.type"
>{{ catagory.type }}</option>
</select>
</div>
</div>
<div class="col-md-8">
<div class="form-group">
<label for="inputTopicTags">Tags</label>
<input type="text" name="description" [(ngModel)]="data.description" class="form-control" id="inputTopicTags" placeholder="Use comma to separate tags">
</div>
</div>
</div>
<div class="row">
<div class="col-auto ml-md-auto">
<a (click)="submitUpdate()"
color="primary"
*ngIf="
data.catagory &&
data.namemovie &&
data.title &&
data.description
" class="btn btn-primary btn-width-lg">Update Post</a>
</div>
</div>
</div>
</form>
</div>
<div class="tt-topic-list tt-offset-top-30"></div>
</div>
</main>
<div id="js-popup-settings" class="tt-popup-settings">
<div class="tt-btn-col-close">
<a href="#">
<span class="tt-icon-title">
<svg>
<use xlink:href="#icon-settings_fill"></use>
</svg>
</span>
<span class="tt-icon-text">
Settings
</span>
<span class="tt-icon-close">
<svg>
<use xlink:href="#icon-cancel"></use>
</svg>
</span>
</a>
</div>
<form class="form-default">
<div class="tt-form-upload">
<div class="row no-gutter">
<div class="col-auto">
<div class="tt-avatar">
<svg>
<use xlink:href="#icon-ava-d"></use>
</svg>
</div>
</div>
<div class="col-auto ml-auto">
<a href="#" class="btn btn-primary">Upload Picture</a>
</div>
</div>
</div>
<div class="form-group">
<label for="settingsUserName">Username</label>
<input type="text" name="name" class="form-control" id="settingsUserName" placeholder="azyrusmax">
</div>
<div class="form-group">
<label for="settingsUserEmail">Email</label>
<input type="text" name="name" class="form-control" id="settingsUserEmail" placeholder="Sample@sample.com">
</div>
<div class="form-group">
<label for="settingsUserPassword">Password</label>
<input type="password" name="name" class="form-control" id="settingsUserPassword" placeholder="************">
</div>
<div class="form-group">
<label for="settingsUserLocation">Location</label>
<input type="text" name="name" class="form-control" id="settingsUserLocation" placeholder="Slovakia">
</div>
<div class="form-group">
<label for="settingsUserWebsite">Website</label>
<input type="text" name="name" class="form-control" id="settingsUserWebsite" placeholder="Sample.com">
</div>
<div class="form-group">
<label for="settingsUserAbout">About</label>
<textarea name="" placeholder="Few words about you" class="form-control" id="settingsUserAbout"></textarea>
</div>
<div class="form-group">
<label for="settingsUserAbout">Notify me via Email</label>
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox01" name="checkbox">
<label for="settingsCheckBox01">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">When someone replies to my thread</span>
</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox02" name="checkbox">
<label for="settingsCheckBox02">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">When someone likes my thread or reply</span>
</label>
</div>
<div class="checkbox-group">
<input type="checkbox" id="settingsCheckBox03" name="checkbox">
<label for="settingsCheckBox03">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">When someone mentions me</span>
</label>
</div>
</div>
<div class="form-group">
<a href="#" class="btn btn-secondary">Save</a>
</div>
</form>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditComponent } from './edit.component';
describe('EditComponent', () => {
let component: EditComponent;
let fixture: ComponentFixture<EditComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EditComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { catagory } from '../../catagory';
import { EditService } from '../service/edit.service';
import { ActivatedRoute } from '@angular/router';
import { Router } from '@angular/router';
@Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
export class EditComponent implements OnInit {
catagorys = catagory;
dataId: any;
data: any;
constructor(private route: ActivatedRoute, private editService: EditService,private router: Router) { }
ngOnInit(): void {
this.route.paramMap.subscribe(params => {
this.dataId = params.get('dataId');
});
this.editService.getReviewsById(this.dataId).subscribe(response => {
this.data = response[0];
console.log(this.data);
});
}
submitUpdate() {
this.editService.putUpdate(this.data).subscribe(response => {
alert('แก้ไขเรียบร้อย'),
this.router.navigateByUrl('/');
});
}
}
<!-- tt-mobile menu -->
<nav class="panel-menu" id="mobile-menu">
<ul>
</ul>
<div class="mm-navbtn-names">
<div class="mm-closebtn">
Close
<div class="tt-icon">
<svg>
<use xlink:href="#icon-cancel"></use>
</svg>
</div>
</div>
<div class="mm-backbtn">Back</div>
</div>
</nav>
<header id="tt-header">
<div class="container">
<div class="row tt-row no-gutters">
<div class="col-auto">
<!-- toggle mobile menu -->
<a class="toggle-mobile-menu" id="tt-desktop-menu">
<svg class="tt-icon">
<use xlink:href="#icon-menu_icon"></use>
</svg>
</a>
<!-- /toggle mobile menu -->
<!-- logo -->
<div class="tt-logo" *ngIf="!user">
<a ><img src="../../assets/images/logo.png" alt=""></a>
</div>
<div class="tt-logo" *ngIf="user">
<a routerLink="/"><img src="../../assets/images/logo.png" alt=""></a>
</div>
<!-- /logo -->
<!-- desctop menu -->
<div class="tt-desktop-menu" *ngIf="!user">
<nav id="tt-desktop-m enu" >
<ul>
<li><a routerLink="/cate"><span>Categories</span></a></li>
<li><a routerLink="/about"><span>About</span></a></li>
</ul>
</nav>
</div>
<div class="tt-desktop-menu" *ngIf="user">
<nav id="tt-desktop-menu" >
<ul>
<li><a routerLink="/create"><span>Create-Topic</span></a></li>
<li><a routerLink="/cate"><span>Categories</span></a></li>
<li><a routerLink="/about"><span>About</span></a></li>
</ul>
</nav>
</div>
<!-- /desctop menu -->
<!-- Search form -->
<div class="tt-search">
<!-- toggle --> <div class="col-auto ml-auto"></div> <div class="col-auto ml-auto"></div> <div class="col-auto ml-auto"></div>
<button class="tt-search-toggle" data-toggle="modal" data-target="#modalAdvancedSearch">
<svg class="tt-icon">
<use xlink:href="#icon-search"></use>
</svg>
</button>
<!-- /toggle -->
<form class="search-wrapper">
<div class="search-form">
<input type="text" class="tt-search__input" placeholder="Search">
<button class="tt-search__btn" type="submit">
<svg class="tt-icon">
<use xlink:href="#icon-search"></use>
</svg>
</button>
<button class="tt-search__close">
<svg class="tt-icon">
<use xlink:href="#cancel"></use>
</svg>
</button>
</div>
</form>
</div>
<!-- /tt-search -->
<!-- /tt-search -->
<div class="col-auto ml-auto">
<div class="tt-user-info d-flex justify-content-center">
<div class="tt-account-btn">
<div class="col-auto ml-auto"></div>
<a class="btn btn-primary" routerLink="/auth/login" *ngIf="!user">Log in</a>
<a [routerLink]="['/auth/register']" *ngIf="!user" class="btn btn-secondary">Sign up</a>
</div>
</div>
</div>
<div class="col-auto ml-auto">
<div class="tt-user-info d-flex justify-content-center" *ngIf="user">
<div class="tt-desktop-menu">
<i>{{ user.fullname }}</i>
</div>
<!-- <div class="tt-avatar-icon tt-size-md ">
<i class="tt-icon "><svg>
<use xlink:href="#icon-ava-u"></use>
</svg></i>
</div> -->
<div class="tt-avatar-icon tt-size-md" (click)="logout()">
<i class="tt-btn-icon"><svg>
<use xlink:href="#icon-exit"></use>
</svg>LogOut</i>
</div>
<!-- <div class="custom-select-01" >
{{ user.fullname }}
<select >
<option >{{ user.fullname }}</option>
<option *ngIf="user?.isAdmin" routerLink="/admin">admin</option>
<option routerLink="logout()" (click)="logout()">logout</option>
</select>
<div routerLink="logout()" (click)="logout()">LogOut</div>
</div> -->
</div>
</div>
</div>
</div>
</div>
</header>
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;
}
}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
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 {
console.warn("out");
this.authService.signOut();
this.router.navigateByUrl('/auth/login');
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HeroComponent } from './hero.component';
describe('HeroComponent', () => {
let component: HeroComponent;
let fixture: ComponentFixture<HeroComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeroComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeroComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hero',
templateUrl: './hero.component.html',
styleUrls: ['./hero.component.css']
})
export class HeroComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<main id="tt-pageContent" class="tt-offset-small">
<div class="container" >
<div class="tt-topic-list" >
<div class="tt-list-header" >
<div class="tt-col-topic">Topic</div>
<div class="tt-col-category">Category</div>
<div class="tt-col-value hide-mobile">Likes</div>
<div class="tt-col-value hide-mobile">Views</div>
<div class="tt-col-value">Activity</div>
<!-- <div class="tt-col-value" *ngIf="user_data._id == review.user_id">Edited</div>
<div class="tt-col-value" *ngIf="user_data._id == review.user_id" >Delete</div> -->
</div>
<div class="tt-item tt-itemselect" *ngFor="let review of reviews">
<div class="tt-col-avatar">
<svg class="tt-icon">
<use xlink:href="#icon-ava-k"></use>
</svg>
</div>
<div class="tt-col-description">
<h6 class="tt-title"><a [routerLink]="['/viewdata', review._id]">
<svg class="tt-icon">
<use xlink:href="#icon-pinned"></use>
</svg>
<strong>{{ review.namemovie }}</strong>
</a> <div [routerLink]="['/edit', review._id]" class="tt-color05 tt-badge btn btn-secondary btn-width-lg" *ngIf="user_data._id == review.user_id">แก้ไข</div>
<div (click)="deleteData(review._id)" class="tt-color06 tt-badge btn btn-primary btn-width-lg" *ngIf="user_data._id == review.user_id">ลบ</div></h6>
<div class="row align-items-center no-gutters">
<div class="col-11">
<ul class="tt-list-badge">
<li class="show-mobile"><a href="#"><span
class="tt-color05 tt-badge">{{ review.catagory }}</span></a></li>
<li><a ><span class="tt-badge">#{{ review.description }}</span></a></li>
</ul>
</div>
</div>
</div>
<div class="tt-col-category"><span class="tt-color01 tt-badge">{{ review.catagory }}</span></div>
<div class="tt-col-value hide-mobile">985</div>
<div class="tt-col-value hide-mobile">25 k</div>
<div class="tt-col-value hide-mobile">{{ review.createdAt | date }}</div>
<!-- <div [routerLink]="['/edit', review._id]" class="tt-col-value btn btn-secondary btn-width-lg" *ngIf="user_data._id == review.user_id">แก้ไข</div>
<div (click)="deleteData(review._id)" class="tt-col-value btn btn-primary btn-width-lg" *ngIf="user_data._id == review.user_id">ลบ</div> -->
</div>
<div class="tt-row-btn">
<button type="button" class="btn-icon js-topiclist-showmore">
<svg class="tt-icon">
<use xlink:href="#icon-load_lore_icon"></use>
</svg>
</button>
</div>
</div>
</div>
</main>
<a routerLink="/create" class="tt-btn-create-topic" >
<span class="tt-icon">
<svg>
<use xlink:href="#icon-create_new"></use>
</svg>
</span>
</a>
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 400px;
margin: 10% auto;
}
.mat-card-title {
font-size: 16px;
}
table {
border-collapse: collapse;
width: 100%;
}
th,
td {
padding: 8px;
text-align: center;
border-bottom: 1px solid #ddd;
}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HomeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { HomeService } from '../service/home.service';
import { catagory } from '../../catagory';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
reviews: any;
user_data: any;
catagorys = catagory;
constructor(private homeService: HomeService, private authService: AuthService) { }
ngOnInit() {
this.fetchData();
this.authService.getUser().subscribe(data => this.user_data = data);
}
fetchData() {
this.homeService.getReviews().subscribe(response => {
this.reviews = response;
});
}
pageroute(data: any) {
console.log(data);
}
deleteData(data: any) {
console.log(data);
this.homeService.deleteReview(data).subscribe((response: {}) => alert('ลบเรียบร้อย'));
this.fetchData();
}
}
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);
}
}
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpErrorResponse,
} from '@angular/common/http';
import { MatSnackBar } from '@angular/material/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);
};
}
import { TestBed } from '@angular/core/testing';
import { CreateService } from './create.service';
describe('CreateService', () => {
let service: CreateService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CreateService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CreateService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
postReview(data: any): Observable<any> {
return this.http.post<any>(`${this.baseUrl}`, JSON.stringify(data), this.httpOptions);
}
}
import { TestBed } from '@angular/core/testing';
import { EditService } from './edit.service';
describe('EditService', () => {
let service: EditService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(EditService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class EditService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
getReviewsById(_id: any): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/get/${_id}`);
}
putUpdate(data: any): Observable<any> {
return this.http.put(`${this.baseUrl}/update/${data._id}`, data);
}
}
import { TestBed } from '@angular/core/testing';
import { HomeService } from './home.service';
describe('HomeService', () => {
let service: HomeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(HomeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HomeService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
getReviews(): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/all`);
}
deleteReview(data: any): Observable<any> {
return this.http.delete<any>(`${this.baseUrl}/delete/${data}`, this.httpOptions);
}
}
import { TestBed } from '@angular/core/testing';
import { ViewdataService } from './viewdata.service';
describe('ViewdataService', () => {
let service: ViewdataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ViewdataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ViewdataService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
getReviews(): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/all`);
}
getReviewsById(_id: any): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/get/${_id}`,this.httpOptions);
}
}
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;
})
);
}
}
export * from './auth.guard';
export * from './user.interface';
export interface User {
_id: string;
fullname: string;
createdAt: string;
roles: string[];
isAdmin: boolean;
}
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();
}));
});
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject, EMPTY } from 'rxjs';
import { tap, pluck } 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);
window.user = user;
}
getUser(): Observable<User | null> {
return this.user$.asObservable();
}
me(): Observable<User> {
const token: string | null = this.tokenStorage.getToken();
if (token === null) {
return EMPTY;
}
return this.http.get<AuthResponse>('/api/auth/me').pipe(
tap(({ user }) => this.setUser(user)),
pluck('user')
);
}
signOut(): void {
this.tokenStorage.signOut();
this.setUser(null);
delete window.user;
}
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> {
return this.me().toPromise();
}
}
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);
}
}
export * from './auth/auth.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatMenuModule } from '@angular/material/menu';
import { MatTabsModule } from '@angular/material/tabs';
import { MatCardModule } from '@angular/material/card';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { MatTreeModule } from '@angular/material/tree';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatProgressBarModule } from '@angular/material/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 {}
<main id="tt-pageContent">
<div class="container">
<div class="tt-single-topic-list">
<div class="tt-item">
<div class="tt-single-topic">
<div class="tt-item-header">
<div class="tt-item-info info-top">
<div class="tt-avatar-icon">
<i class="tt-icon"><svg><use xlink:href="#icon-ava-d"></use></svg></i>
</div>
<div class="tt-avatar-title">
<a >{{ data._id }}</a>
</div>
<a href="#" class="tt-info-time">
<i class="tt-icon"><svg><use xlink:href="#icon-time"></use></svg></i>{{ data.createAt| date }}
</a>
</div>
<h2 class="tt-item-title">
<a ><strong>{{ data.namemovie}}</strong></a>
</h2>
<div class="tt-item-tag">
<ul class="tt-list-badge">
<li><a ><span class="tt-color03 tt-badge">#{{ data.description }}</span></a></li>
<!-- <li><a href="#"><span class="tt-badge">themeforest</span></a></li>
<li><a href="#"><span class="tt-badge">elements</span></a></li> -->
</ul>
</div>
</div>
<div class="tt-item-description">
<p>
{{ data.title }}
</p>
</div>
<div class="tt-item-info info-bottom">
<a href="#" class="tt-icon-btn">
<i class="tt-icon"><svg><use xlink:href="#icon-like"></use></svg></i>
<span class="tt-text">671</span>
</a>
<a href="#" class="tt-icon-btn">
<i class="tt-icon"><svg><use xlink:href="#icon-dislike"></use></svg></i>
<span class="tt-text">39</span>
</a>
<a href="#" class="tt-icon-btn">
<i class="tt-icon"><svg><use xlink:href="#icon-favorite"></use></svg></i>
<span class="tt-text">12</span>
</a>
<div class="col-separator"></div>
<a href="#" class="tt-icon-btn tt-hover-02 tt-small-indent">
<i class="tt-icon"><svg><use xlink:href="#icon-share"></use></svg></i>
</a>
<a href="#" class="tt-icon-btn tt-hover-02 tt-small-indent">
<i class="tt-icon"><svg><use xlink:href="#icon-flag"></use></svg></i>
</a>
<a href="#" class="tt-icon-btn tt-hover-02 tt-small-indent">
<i class="tt-icon"><svg><use xlink:href="#icon-reply"></use></svg></i>
</a>
</div>
</div>
</div>
<div class="tt-wrapper-inner">
<h4 class="tt-title-separator"><span>สามารถแสดงความคิดเห็นได้</span></h4>
</div>
<div class="tt-item">
<div class="tt-single-topic">
<div class="tt-item-description">
<div class="topic-inner-list">
<div class="topic-inner">
<div class="topic-inner-title">
<div class="topic-inner-avatar">
<i class="tt-icon"><svg><use xlink:href="#icon-ava-s"></use></svg></i>
</div>
<div class="topic-inner-title"><a href="#">summit92</a></div>
</div>
<div class="topic-inner-description">
Finally!<br>
Are there any special recommendations for design or an updated guide that includes new preview sizes, including retina displays?
</div>
</div>
</div>
</div>
<div class="tt-item-info info-bottom">
<a href="#" class="tt-icon-btn">
<i class="tt-icon"><svg><use xlink:href="#icon-like"></use></svg></i>
<span class="tt-text">671</span>
</a>
<a href="#" class="tt-icon-btn">
<i class="tt-icon"><svg><use xlink:href="#icon-dislike"></use></svg></i>
<span class="tt-text">39</span>
</a>
<a href="#" class="tt-icon-btn">
<i class="tt-icon"><svg><use xlink:href="#icon-favorite"></use></svg></i>
<span class="tt-text">12</span>
</a>
<div class="col-separator"></div>
<a href="#" class="tt-icon-btn tt-hover-02 tt-small-indent">
<i class="tt-icon"><svg><use xlink:href="#icon-time"></use></svg></i>
6 Jan,2019
</a>
</div>
</div>
</div>
</div>
<div class="tt-wrapper-inner">
<div class="pt-editor form-default">
<h6 class="pt-title">Post Your Reply</h6>
<div class="form-group">
<textarea name="message" class="form-control" rows="5" placeholder="Lets get started"></textarea>
</div>
<div class="pt-row">
<div class="col-auto">
<div class="checkbox-group">
<input type="checkbox" id="checkBox21" name="checkbox" checked="">
<label for="checkBox21">
<span class="check"></span>
<span class="box"></span>
<span class="tt-text">Subscribe to this topic.</span>
</label>
</div>
</div>
<div class="col-auto">
<a href="#" class="btn btn-secondary btn-width-lg">แสดงความคิดเห็น</a>
</div>
</div>
</div>
</div>
</div>
</main>
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewdataComponent } from './viewdata.component';
describe('ViewdataComponent', () => {
let component: ViewdataComponent;
let fixture: ComponentFixture<ViewdataComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ViewdataComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ViewdataComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { create } from 'domain';
import { ViewdataService } from '../service/viewdata.service';
import { catagory } from '../../catagory';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-viewdata',
templateUrl: './viewdata.component.html',
styleUrls: ['./viewdata.component.css']
})
export class ViewdataComponent implements OnInit {
constructor(private route: ActivatedRoute, private reviewdataService: ViewdataService,private authService: AuthService) { }
dataId: any;
data: any;
user_data: any;
catagorys = catagory;
ngOnInit(): void {
this.authService.getUser().subscribe(data => this.user_data = data);
this.route.paramMap.subscribe(params => {
this.dataId = params.get('dataId');
});
this.reviewdataService.getReviewsById(this.dataId).subscribe(response => {
this.data = response[0];
console.warn(this.data);
});
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment