This commit is contained in:
2025-11-27 16:33:42 +01:00
commit ca9ac29ea4
19 changed files with 3346 additions and 0 deletions

135
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,8 @@
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: './database.sqlite'
});
module.exports = sequelize;

View 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

Binary file not shown.

38
models/customer.js Normal file
View 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

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>