initial commit
This commit is contained in:
35
app/Dockerfile
Normal file
35
app/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Comments are provided throughout this file to help you get started.
|
||||||
|
# If you need more help, visit the Dockerfile reference guide at
|
||||||
|
# https://docs.docker.com/engine/reference/builder/
|
||||||
|
|
||||||
|
ARG NODE_VERSION=19.5.0
|
||||||
|
|
||||||
|
FROM node:${NODE_VERSION}-alpine
|
||||||
|
|
||||||
|
# Use production node environment by default.
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Download dependencies as a separate step to take advantage of Docker's caching.
|
||||||
|
# Leverage a cache mount to /root/.npm to speed up subsequent builds.
|
||||||
|
# Leverage a bind mounts to package.json and package-lock.json to avoid having to copy them into
|
||||||
|
# into this layer.
|
||||||
|
RUN --mount=type=bind,source=package.json,target=package.json \
|
||||||
|
--mount=type=bind,source=package-lock.json,target=package-lock.json \
|
||||||
|
--mount=type=cache,target=/root/.npm \
|
||||||
|
npm ci --omit=dev
|
||||||
|
|
||||||
|
# Run the application as a non-root user.
|
||||||
|
USER node
|
||||||
|
|
||||||
|
# Copy the rest of the source files into the image.
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose the port that the application listens on.
|
||||||
|
EXPOSE 3001
|
||||||
|
|
||||||
|
# Run the application.
|
||||||
|
CMD npm run dev
|
||||||
3
app/config/keys.js
Normal file
3
app/config/keys.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
mongoProdURI: 'mongodb://todo-database:27017/todoapp',
|
||||||
|
};
|
||||||
15
app/models/Todo.js
Normal file
15
app/models/Todo.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const TodoSchema = new Schema({
|
||||||
|
task: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Todo = mongoose.model('todos', TodoSchema);
|
||||||
1384
app/package-lock.json
generated
Normal file
1384
app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
app/package.json
Normal file
20
app/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "multi-container-apps",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js",
|
||||||
|
"dev": "nodemon server.js -e js,ejs,mjs,cjs,json"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
|
"ejs": "^3.1.9",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"mongoose": "^7.1.0",
|
||||||
|
"nodemon": "^3.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/routes/front.js
Normal file
34
app/routes/front.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const Todo = require('./../models/Todo');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// Home page route
|
||||||
|
router.get('/', async (req, res) => {
|
||||||
|
|
||||||
|
const todos = await Todo.find()
|
||||||
|
res.render("todos", {
|
||||||
|
tasks: (Object.keys(todos).length > 0 ? todos : {})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST - Submit Task
|
||||||
|
router.post('/', (req, res) => {
|
||||||
|
const newTask = new Todo({
|
||||||
|
task: req.body.task
|
||||||
|
});
|
||||||
|
|
||||||
|
newTask.save()
|
||||||
|
.then(task => res.redirect('/'))
|
||||||
|
.catch(err => console.log(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST - Destroy todo item
|
||||||
|
router.post('/todo/destroy', async (req, res) => {
|
||||||
|
const taskKey = req.body._key;
|
||||||
|
const err = await Todo.findOneAndRemove({_id: taskKey})
|
||||||
|
res.redirect('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
29
app/server.js
Normal file
29
app/server.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
const mongoose = require('mongoose');
|
||||||
|
const bodyParse = require('body-parser');
|
||||||
|
const app = require('express')();
|
||||||
|
const moment = require('moment');
|
||||||
|
|
||||||
|
// Fontend route
|
||||||
|
const FrontRouter = require('./routes/front');
|
||||||
|
|
||||||
|
// Set ejs template engine
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
|
app.use(bodyParse.urlencoded({ extended: false }));
|
||||||
|
app.locals.moment = moment;
|
||||||
|
|
||||||
|
// Database connection
|
||||||
|
const db = require('./config/keys').mongoProdURI;
|
||||||
|
mongoose
|
||||||
|
.connect(db, { useNewUrlParser: true })
|
||||||
|
.then(() => console.log(`Mongodb Connected`))
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
|
||||||
|
|
||||||
|
app.use(FrontRouter);
|
||||||
|
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3001;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server listening on port ${PORT}`);
|
||||||
|
});
|
||||||
70
app/views/todos.ejs
Normal file
70
app/views/todos.ejs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Todo App</title>
|
||||||
|
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="app">
|
||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-dark bg-primary">
|
||||||
|
<span class="navbar-brand mb-0 h1">Todo App</span>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 m-auto pt-4">
|
||||||
|
<form method="POST" action="/" autocomplete="off">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 form-group">
|
||||||
|
<label for="todo">Enter your task</label>
|
||||||
|
<input type="text" name="task" class="form-control" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<% if(Object.keys(tasks).length> 0) { %>
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<% tasks.forEach(todo=> { %>
|
||||||
|
<li class="nav-item">
|
||||||
|
<div class="d-flex justify-content-between py-1">
|
||||||
|
<div class="d-flex flex-row">
|
||||||
|
<div>
|
||||||
|
<%= todo.task %>
|
||||||
|
<p class="text-muted"><small>
|
||||||
|
<%= moment(todo.created_at).fromNow() %>
|
||||||
|
</small></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="javascript:;" onclick="this.children[0].submit()"
|
||||||
|
class="text-danger">
|
||||||
|
<form method="POST" action="/todo/destroy">
|
||||||
|
<input type="hidden" name="_key" value="<%= todo._id %>" />
|
||||||
|
</form>
|
||||||
|
<i class="fa fa-trash-o"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="text-center"><strong>Please add some task.</strong></div>
|
||||||
|
<% }%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user