init
This commit is contained in:
135
.gitignore
vendored
Normal file
135
.gitignore
vendored
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# MacOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# ---> Node
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
23
LICENSE
Normal file
23
LICENSE
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Copyright <yyyy, yyyy> The Open Group
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this software and
|
||||||
|
its documentation for any purpose is hereby granted without fee,
|
||||||
|
provided that the above copyright notice appear in all copies and that
|
||||||
|
both that copyright notice and this permission notice appear in
|
||||||
|
supporting documentation.
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||||
|
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
|
||||||
|
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of The Open Group
|
||||||
|
shall not be used in advertising or otherwise to promote the sale, use
|
||||||
|
or other dealings in this Software without prior written authorization
|
||||||
|
from The Open Group.
|
||||||
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Przykład MVC CRUD w Node.js + Express + SQLite + Sequelize
|
||||||
|
|
||||||
|
Przykład aplikacji CRUD w NodeJS, framework Express i ORM
|
||||||
|
|
||||||
|
## Struktura katalogów
|
||||||
|
```
|
||||||
|
my-app/
|
||||||
|
├── app.js
|
||||||
|
├── package.json
|
||||||
|
├── config/
|
||||||
|
│ └── database.js
|
||||||
|
├── models/
|
||||||
|
│ └── customer.js
|
||||||
|
├── controllers/
|
||||||
|
│ └── customerController.js
|
||||||
|
├── routes/
|
||||||
|
│ └── customerRoutes.js
|
||||||
|
└── views/
|
||||||
|
├── layout.ejs
|
||||||
|
├── index.ejs
|
||||||
|
├── create.ejs
|
||||||
|
├── edit.ejs
|
||||||
|
└── show.ejs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Instalacja
|
||||||
|
|
||||||
|
Po sklonowaniu repozytorium otwórz folder projektu w Visual Studio Code i w temrminalu:
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
npm start
|
||||||
|
|
||||||
|
## Uruchomienie serwera
|
||||||
|
|
||||||
|
npm run start
|
||||||
|
|
||||||
|
http://localhost:3000
|
||||||
|
|
||||||
35
app.js
Normal file
35
app.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
const methodOverride = require('method-override');
|
||||||
|
const expressLayouts = require('express-ejs-layouts');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const db = require('./config/database');
|
||||||
|
|
||||||
|
// Test połączenia z bazą
|
||||||
|
db.authenticate()
|
||||||
|
.then(() => console.log('Połączono z SQLite.'))
|
||||||
|
.catch(err => console.error('Błąd połączenia:', err));
|
||||||
|
|
||||||
|
// Inicjalizacja Express
|
||||||
|
const app = express();
|
||||||
|
// Uzycie expressLayout i konfiguracja ejs
|
||||||
|
app.use(expressLayouts);
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
|
app.set('layout', 'layout');
|
||||||
|
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }));
|
||||||
|
app.use(methodOverride('_method'));
|
||||||
|
|
||||||
|
// Routing
|
||||||
|
const customerRoutes = require('./routes/customerRoutes');
|
||||||
|
app.use('/customers', customerRoutes);
|
||||||
|
|
||||||
|
// Strona główna przekierowuje do listy klientów
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.redirect('/customers');
|
||||||
|
});
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
app.listen(PORT, () => console.log(`Serwer działa na porcie ${PORT}`));
|
||||||
8
config/database.js
Normal file
8
config/database.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const { Sequelize } = require('sequelize');
|
||||||
|
|
||||||
|
const sequelize = new Sequelize({
|
||||||
|
dialect: 'sqlite',
|
||||||
|
storage: './database.sqlite'
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = sequelize;
|
||||||
35
controllers/customerController.js
Normal file
35
controllers/customerController.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const Customer = require('../models/customer');
|
||||||
|
|
||||||
|
exports.list = async (req, res) => {
|
||||||
|
const customers = await Customer.findAll();
|
||||||
|
res.render('index', { customers });
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.show = async (req, res) => {
|
||||||
|
const customer = await Customer.findByPk(req.params.id);
|
||||||
|
res.render('show', { customer });
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.createForm = (req, res) => {
|
||||||
|
res.render('create');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.create = async (req, res) => {
|
||||||
|
await Customer.create(req.body);
|
||||||
|
res.redirect('/customers');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.editForm = async (req, res) => {
|
||||||
|
const customer = await Customer.findByPk(req.params.id);
|
||||||
|
res.render('edit', { customer });
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.update = async (req, res) => {
|
||||||
|
await Customer.update(req.body, { where: { id: req.params.id } });
|
||||||
|
res.redirect('/customers');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.delete = async (req, res) => {
|
||||||
|
await Customer.destroy({ where: { id: req.params.id } });
|
||||||
|
res.redirect('/customers');
|
||||||
|
};
|
||||||
BIN
database.sqlite
Normal file
BIN
database.sqlite
Normal file
Binary file not shown.
38
models/customer.js
Normal file
38
models/customer.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
const sequelize = require('../config/database');
|
||||||
|
|
||||||
|
const Customer = sequelize.define('Customer', {
|
||||||
|
firstName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
postalCode: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
city: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
phone: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
nip: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Synchronizacja modelu z bazą
|
||||||
|
Customer.sync();
|
||||||
|
|
||||||
|
module.exports = Customer;
|
||||||
2865
package-lock.json
generated
Normal file
2865
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "nodejs-express-orm-crud",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Przykład aplikacji CRUD w NodeJS, framework Express i ORM",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js",
|
||||||
|
"dev": "nodemon app.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.klich.net.pl/skyer/Nodejs-express-orm-crud.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"author": "Leszek Klich",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^2.2.0",
|
||||||
|
"ejs": "^3.1.10",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"express-ejs-layouts": "^2.5.1",
|
||||||
|
"method-override": "^3.0.0",
|
||||||
|
"sequelize": "^6.37.7",
|
||||||
|
"sqlite3": "^5.1.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^3.1.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
routes/customerRoutes.js
Normal file
13
routes/customerRoutes.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const controller = require('../controllers/customerController');
|
||||||
|
|
||||||
|
router.get('/', controller.list);
|
||||||
|
router.get('/new', controller.createForm);
|
||||||
|
router.post('/', controller.create);
|
||||||
|
router.get('/:id', controller.show);
|
||||||
|
router.get('/:id/edit', controller.editForm);
|
||||||
|
router.put('/:id', controller.update);
|
||||||
|
router.delete('/:id', controller.delete);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
4
views/create.ejs
Normal file
4
views/create.ejs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<form action="/customers" method="post">
|
||||||
|
<%- include('form', { customer: {} }) %>
|
||||||
|
<button class="btn btn-success">Zapisz</button>
|
||||||
|
</form>
|
||||||
4
views/edit.ejs
Normal file
4
views/edit.ejs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<form action="/customers/<%= customer.id %>?_method=PUT" method="post">
|
||||||
|
<%- include('form', { customer }) %>
|
||||||
|
<button class="btn btn-primary">Aktualizuj</button>
|
||||||
|
</form>
|
||||||
12
views/example.ejs
Normal file
12
views/example.ejs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!-- views/przyklad.ejs -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title><%= title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><%= title %></h1>
|
||||||
|
<p>Liczba: <%= liczba %></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
28
views/form.ejs
Normal file
28
views/form.ejs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Imię</label>
|
||||||
|
<input type="text" name="firstName" class="form-control" value="<%= customer.firstName || '' %>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Nazwisko</label>
|
||||||
|
<input type="text" name="lastName" class="form-control" value="<%= customer.lastName || '' %>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Adres</label>
|
||||||
|
<input type="text" name="address" class="form-control" value="<%= customer.address || '' %>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Kod pocztowy</label>
|
||||||
|
<input type="text" name="postalCode" class="form-control" value="<%= customer.postalCode || '' %>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Miasto</label>
|
||||||
|
<input type="text" name="city" class="form-control" value="<%= customer.city || '' %>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Telefon</label>
|
||||||
|
<input type="text" name="phone" class="form-control" value="<%= customer.phone || '' %>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">NIP</label>
|
||||||
|
<input type="text" name="nip" class="form-control" value="<%= customer.nip || '' %>" required>
|
||||||
|
</div>
|
||||||
29
views/index.ejs
Normal file
29
views/index.ejs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<a href="/customers/new" class="btn btn-primary mb-3">Dodaj klienta</a>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Imię</th>
|
||||||
|
<th>Nazwisko</th>
|
||||||
|
<th>Miasto</th>
|
||||||
|
<th>Telefon</th>
|
||||||
|
<th>Akcje</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% customers.forEach(c => { %>
|
||||||
|
<tr>
|
||||||
|
<td><%= c.firstName %></td>
|
||||||
|
<td><%= c.lastName %></td>
|
||||||
|
<td><%= c.city %></td>
|
||||||
|
<td><%= c.phone %></td>
|
||||||
|
<td>
|
||||||
|
<a href="/customers/<%= c.id %>" class="btn btn-sm btn-info">Pokaż</a>
|
||||||
|
<a href="/customers/<%= c.id %>/edit" class="btn btn-sm btn-warning">Edytuj</a>
|
||||||
|
<form action="/customers/<%= c.id %>?_method=DELETE" method="post" style="display:inline">
|
||||||
|
<button class="btn btn-sm btn-danger" onclick="return confirm('Usunąć?')">Usuń</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% }) %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
15
views/layout.ejs
Normal file
15
views/layout.ejs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Lista klientów</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
<body class="p-3">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="mb-4">Lista klientów</h1>
|
||||||
|
<%- body %>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
10
views/show.ejs
Normal file
10
views/show.ejs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item"><strong>Imię:</strong> <%= customer.firstName %></li>
|
||||||
|
<li class="list-group-item"><strong>Nazwisko:</strong> <%= customer.lastName %></li>
|
||||||
|
<li class="list-group-item"><strong>Adres:</strong> <%= customer.address %></li>
|
||||||
|
<li class="list-group-item"><strong>Kod pocztowy:</strong> <%= customer.postalCode %></li>
|
||||||
|
<li class="list-group-item"><strong>Miasto:</strong> <%= customer.city %></li>
|
||||||
|
<li class="list-group-item"><strong>Telefon:</strong> <%= customer.phone %></li>
|
||||||
|
<li class="list-group-item"><strong>NIP:</strong> <%= customer.nip %></li>
|
||||||
|
</ul>
|
||||||
|
<a href="/customers" class="btn btn-link mt-3">Powrót</a>
|
||||||
21
views/users.ejs
Normal file
21
views/users.ejs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Użytkownicy</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Lista użytkowników</h1>
|
||||||
|
<ul>
|
||||||
|
<% users.forEach(function(user) { %>
|
||||||
|
<li><%= user.id %>: <%= user.name %></li>
|
||||||
|
<% }) %>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<% if (config.showEmails) { %>
|
||||||
|
<p>Adresy e-mail są widoczne</p>
|
||||||
|
<% } else { %>
|
||||||
|
<p>Adresy e-mail ukryte (strona <%= config.page %>)</p>
|
||||||
|
<% } %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user