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">