최동원

.

......@@ -9,6 +9,7 @@ var indexRouter = require('./routes/index');
var userRouter = require('./routes/userlogin/user');
var loginRouter = require('./routes/userlogin/login');
var registerRouter = require('./routes/userlogin/register');
var folderRouter = require('./routes/folders');
var passport = require('passport');
......@@ -17,19 +18,19 @@ var config = require('./routes/modules/config');
//port
passport.serializeUser(function(user, done) {
console.log('serialized');
done(null, user);
console.log('serialized');
done(null, user);
});
passport.deserializeUser(function(user, done) {
console.log('deserialized');
done(null, user);
console.log('deserialized');
done(null, user);
});
var app = express();
// view engine setup
app.set('views', [path.join(__dirname, 'views'),path.join(__dirname ,'dist')]);
app.set('views', [path.join(__dirname, 'views'), path.join(__dirname, 'dist')]);
// app.set('view engine', 'ejs');
app.use(logger('dev'));
......@@ -39,9 +40,9 @@ app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'mykey',
saveUninitialized: true,
resave: true
secret: 'mykey',
saveUninitialized: true,
resave: true
}));
app.use(express.static('public'));
......@@ -54,22 +55,23 @@ app.use('/api/', indexRouter);
app.use('/api/user', userRouter);
app.use('/api/login', loginRouter);
app.use('/api/RegistUser', registerRouter);
app.use('/api/folder', folderRouter);
//app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
module.exports = app;
\ No newline at end of file
......
......@@ -153,6 +153,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
......@@ -200,6 +205,16 @@
"concat-map": "0.0.1"
}
},
"buffer": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4",
"isarray": "^1.0.0"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
......@@ -438,6 +453,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"events": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
},
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
......@@ -650,6 +670,11 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
......@@ -720,6 +745,11 @@
"minimatch": "^3.0.4"
}
},
"jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"js-stringify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
......@@ -829,6 +859,11 @@
"brace-expansion": "^1.1.7"
}
},
"moment": {
"version": "2.26.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz",
"integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw=="
},
"morgan": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz",
......@@ -1066,15 +1101,26 @@
"integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA=="
},
"punycode": {
<<<<<<< HEAD
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
=======
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
>>>>>>> 677e924ea1f8968d4b256c5c0ca83390eb370802
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
......@@ -1173,6 +1219,11 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
......@@ -1276,6 +1327,16 @@
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
<<<<<<< HEAD
=======
},
"dependencies": {
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
}
>>>>>>> 677e924ea1f8968d4b256c5c0ca83390eb370802
}
},
"tunnel-agent": {
......@@ -1342,6 +1403,25 @@
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"requires": {
"punycode": "^2.1.0"
<<<<<<< HEAD
=======
},
"dependencies": {
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
}
}
},
"url": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
"requires": {
"punycode": "1.3.2",
"querystring": "0.2.0"
>>>>>>> 677e924ea1f8968d4b256c5c0ca83390eb370802
}
},
"util-deprecate": {
......@@ -1355,9 +1435,15 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
<<<<<<< HEAD
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
=======
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
>>>>>>> 677e924ea1f8968d4b256c5c0ca83390eb370802
},
"vary": {
"version": "1.1.2",
......@@ -1398,6 +1484,20 @@
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
"integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
},
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
},
"yargs": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
......
......@@ -6,6 +6,7 @@
"start": "node ./bin/www"
},
"dependencies": {
"aws-sdk": "^2.683.0",
"cookie-parser": "~1.4.4",
"crypto-js": "^4.0.0",
"debug": "~2.6.9",
......@@ -13,6 +14,7 @@
"express": "~4.16.1",
"express-session": "^1.17.1",
"http-errors": "~1.6.3",
"moment": "^2.26.0",
"morgan": "~1.9.1",
"mysql": "^2.18.1",
"passport": "^0.4.1",
......
const express = require('express');
const router = express.Router();
const AWS = require("aws-sdk");
const moment = require("moment");
const BUCKET_NAME = "hong-s3-cloud";
let curPath = "";
let user_id = "";
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: "ap-northeast-2"
});
router.get('/show', function(req, res, next) {
console.log(req.query);
user_id = req.query.id;
curPath = req.query.cur;
folders = {}
let checkfolder = 'SELECT * FROM folders WHERE location = ? AND user_id = ?;';
connection.query(checkfolder, [curPath, user_id], function(err, rows, fields) {
if (rows.length != 0) {
res.status(200).send({
folders: rows,
cur: curPath
})
} else {
res.send({ error: "Does not exist" });
}
});
});
router.post('/makefolder', function(req, res, next) {
user_id = req.body.user_id;
let cur = req.body.cur;
curPath = user_id + cur;
let folder_name = req.body.folder_name;
let date = moment().format();
let params = {
Bucket: BUCKET_NAME,
Key: curPath + folder_name + '/',
Body: "",
ACL: "public-read-write"
};
let checksql = 'SELECT * FROM folders WHERE location = ? AND folder_name = ?;';
console.log(req.body)
connection.query(checksql, [cur, folder_name], function(err, rows, fields) {
if (rows.length == 0) {
s3.putObject(params, function(err, data) {
if (err) {
console.log('s3 error');
throw err;
} else {
console.log(data);
}
});
let sql = 'INSERT INTO folders (folder_name,location,user_id,created) values (?,?,?,?);';
let values = [folder_name, cur, user_id, date];
connection.query(sql, values, function(err, result, field) {
if (err) {
console.log('insert error');
throw err;
} else {
folders = {}
let checkfolder = 'SELECT * FROM folders WHERE location = ? AND user_id = ?;';
connection.query(checkfolder, [cur, user_id], function(err, rows, fields) {
if (rows.length != 0) {
res.status(200).send({
folders: rows,
cur: curPath
})
} else {
res.send({ error: "Does not exist" });
}
});
}
});
} else {
res.status(404).send({ error: "same name error" });
}
});
});
router.post('/delfolder', function(req, res, next) {
user_id = req.body.user_id;
curPath = user_id + req.body.cur;
let folder_name = req.body.folder_name;
let params = {
Bucket: BUCKET_NAME + curPath,
Key: folder_name + '/'
};
let checksql = 'SELECT * FROM folders WHERE location = ? AND folder_name = ?;';
let values = [curPath, folder_name];
connection.query(checksql, values, function(err, rows, fields) {
if (rows.length != 0) {
s3.deleteObject(params, function(err, data) {
if (err) {
//throw err;
} else {
let sql = 'DELETE FROM folders WHERE location = ? AND folder_name = ?;';
connection.query(sql, values, function(err, result, field) {
if (err) {
//throw err;
} else {
folders = {}
let checkfolder = 'SELECT * FROM folders WHERE location = ? AND user_id = ?;';
connection.query(checkfolder, [cur, user_id], function(err, rows, fields) {
if (rows.length != 0) {
res.status(200).send({
folders: rows,
cur: curPath
})
} else {
res.send({ error: "Does not exist" });
}
});
}
});
}
});
} else {
res.send({ error: "Does not exist" });
}
});
});
router.post('/move', function(req, res, next) {
user_id = req.body.user_id;
curPath = user_id + req.body.cur;
let name = req.body.mfile;
let newPath = req.body.newPath;
let checkfolder = 'SELECT * FROM folders WHERE location = ? AND folder_name = ?;';
if (req.body.isfolder) {
connection.query(checkfolder, [curPath, name], function(err1, rows, fields) {
if (rows.length != 0) {
let copy_params = {
Bucket: BUCKET_NAME + curPath,
CopySource: BUCKET_NAME + curPath + file + '/',
Key: newPath + file + '/'
};
let del_params = {
Bucket: BUCKET_NAME + curPath,
Key: file + '/'
};
s3.copyObject(copy_params, function(err, data) {
console.log(err, data);
});
s3.deleteObject(del_params, function(err, data) {
console.log(err, data);
});
let values = [newPath, curPath, name];
let updatesql = 'UPDATE folders SET location = ? WHERE location = ? AND folder_name = ?;';
connection.query(updatesql, values, function(err3, result, field) {
if (err3) {
throw err;
} else {
folders = {}
connection.query(checkfolder, [cur, user_id], function(err, rows, fields) {
if (rows.length != 0) {
res.status(200).send({
folders: rows,
cur: curPath
})
} else {
res.send({ error: "Does not exist" });
}
});
}
});
} else {
res.send({ error: "Does not exist" });
}
});
} else {
let checkfile = 'SELECT * FROM files WHERE location = ? AND file_name = ?';
connection.query(checkfile, [curPath, name], function(err1, rows, fields) {
if (rows.length != 0) {
let copy_params = {
Bucket: BUCKET_NAME + curPath,
CopySource: BUCKET_NAME + curPath + file,
Key: newPath + file
};
let del_params = {
Bucket: BUCKET_NAME + curPath,
Key: file
};
s3.copyObject(copy_params, function(err, data) {
console.log(err, data);
});
s3.deleteObject(del_params, function(err, data) {
console.log(err, data);
});
let values = [newPath, curPath, name];
let updatesql = 'UPDATE files SET location = ? WHERE location = ? AND file_name = ?;';
connection.query(updatesql, values, function(err3, result, field) {
if (err3) {
throw err;
} else {
folders = {}
connection.query(checkfolder, [cur, user_id], function(err, rows, fields) {
if (rows.length != 0) {
res.status(200).send({
folders: rows,
cur: curPath
})
} else {
res.send({ error: "Does not exist" });
}
});
}
});
} else {
res.send({ error: "Does not exist" });
}
});
}
});
router.post('/search/:target', function(req, res, next) {
user_id = req.params.id;
let cur = req.params.cur;
folders = {}
let checkfolder = 'SELECT * FROM folders WHERE location = ? AND user_id = ?;';
connection.query(checkfolder, [cur, user_id], function(err, rows, fields) {
if (rows.length != 0) {
res.status(200).send({
folders: folders
})
} else {
res.send({ error: "Does not exist" });
}
});
});
module.exports = router;
\ No newline at end of file
......@@ -5,21 +5,26 @@ const instance = axios.create({
});
function registerUser(userData) {
// const url = 'http://localhost:3000/api/signup'
// const url = 'http://localhost:3000/api/signup'
return axios.post('/api/RegistUser', userData);
}
function loginUser(userData) {
}
function loginUser(userData) {
// const url = 'http://localhost:3000/api/login'
return axios.post('/api/login', userData);
}
function dropbox(userData){
return axios.get(`/api/folder/show/${userData}`);
}
function makeFolder(folderData){
return axios.post('/api/folder/makefolder', folderData);
function folder(curData) {
return axios.get('/api/folder/show', {
params: {
id: curData.id,
cur: curData.cur
}
});
}
function makeFolder(folderData) {
return axios.post('/api/folder/makefolder', folderData);
}
export { registerUser, loginUser, dropbox, makeFolder };
\ No newline at end of file
export { registerUser, loginUser, folder, makeFolder };
\ No newline at end of file
......
......@@ -12,10 +12,8 @@
hide-details
></v-text-field>
</v-toolbar>
<v-list two-line subheader>
<v-subheader inset>Folders</v-subheader>
<v-list-item
v-for="item in this.$store.getters.folderL"
:key="item.title"
......@@ -24,22 +22,17 @@
<v-list-item-avatar>
<v-icon>mdi-folder</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item.folder_name"></v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn icon>
<v-icon color="grey lighten-1">mdi-information</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
<v-divider inset></v-divider>
<v-subheader inset>Files</v-subheader>
<v-list-item
v-for="item in this.$store.getters.fileL"
:key="item.title"
......@@ -48,11 +41,9 @@
<v-list-item-avatar>
<v-icon> mdi-file</v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-text="item"></v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn icon>
<v-icon color="grey lighten-1">mdi-information</v-icon>
......@@ -81,7 +72,6 @@
>
{{ text }}
</v-chip>
<span
v-else-if="index === 2"
class="overline grey--text text--darken-3 mx-2"
......@@ -89,7 +79,7 @@
+{{ files.length - 2 }} File(s)
</span>
</template>
</v-file-input>
</v-file-input>
<v-btn
bottom
color="blue"
......@@ -112,7 +102,7 @@
<v-container>
<div>
<v-icon>mdi-folder</v-icon>
<v-text-field placeholder="name" id="foldername" type="text" v-model="foldername" ></v-text-field>
<v-text-field placeholder="name" id="foldername" type="text" v-model="foldername"></v-text-field>
</div>
</v-container>
<v-card-actions>
......@@ -133,54 +123,57 @@
</template>
<script>
import { dropbox, makeFolder } from '../api/index';
import { folder, makeFolder } from '../api/index';
export default {
data() {
return {
foldername: '',
foldername:'',
folders: [],
files: [],
search:'',
id: '',
dialog: false,
dialog:false
}
},
async created(){
try {
const userData = this.$store.state.id;
console.log(this.id);
const response = await dropbox(userData);
const curData = {
id : this.$store.state.id,
cur: this.$store.state.cur
}
const response = await folder(curData);
console.log(response);
this.$store.commit('setFolder', response.data.folders);
this.$store.commit('setFile', response.data.files);
this.$store.commit('setCur', response.data.cur);
} catch (error) {
console.log("에러");
console.log(error.response.data);
console.log(error.response.data);
}
},
methods: {
initFolderName(){
this.foldername = '';
},
async makeF(){
try {
const folderData = {
user_id : this.$store.state.id,
cur : this.$store.state.id,
folder_name : this.foldername
};
const response = await makeFolder(folderData);
this.$store.commit('setFolder', response.data.folders);
console.log("폴더 생성 완료");
} catch (error) {
console.log("에러");
console.log(error.response.data);
} finally{
this.initFolderName();
this.dialog = false;
}
}
}
methods: {
initFolderName(){
this.foldername = '';
},
async makeF(){
try {
const folderData = {
user_id : this.$store.state.id,
cur : this.$store.state.cur,
folder_name : this.foldername
};
const response = await makeFolder(folderData);
console.log(response.data)
console.log("폴더 생성 완료");
this.$store.commit('setFolder', response.data.folders);
} catch (error) {
console.log("에러");
console.log(error.response.data);
} finally{
this.initFolderName();
this.dialog = false;
}
}
}
}
</script>
......
......@@ -4,38 +4,44 @@ import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
id: '',
folders: [],
files: [],
cur:'/',
},
mutations: {
setId(state, userid){
state.id = userid;
state: {
id: '',
folders: {},
files: {},
cur: '/',
},
clearid(state){
state.id= '';
mutations: {
setId(state, userid) {
state.id = userid;
},
clearid(state) {
state.id = '';
},
setFolder(state, folderlist) {
state.folders = folderlist;
},
setFile(state, filelist) {
state.files = filelist;
},
setCur(state, cur) {
state.cur = cur;
}
},
setFolder(state, folderlist){
state.folders = folderlist;
},
setFile(state, filelist){
state.files = filelist;
},
},
getters: {
isLogin(state){
return state.id !== '';
},
userID(state){
return state.id;
},
folderL(state){
return state.folders;
},
fileL(state){
return state.files;
getters: {
isLogin(state) {
return state.id !== '';
},
userID(state) {
return state.id;
},
folderL(state) {
return state.folders;
},
fileL(state) {
return state.files;
},
cur(state) {
return state.cur;
}
}
}
})
})
\ No newline at end of file
......