Last active
March 22, 2025 09:42
-
-
Save RajaniCode/acd33ce0bb7f3ede25d110d51aed0fc2 to your computer and use it in GitHub Desktop.
Podman Node.js MongoDB MinIO
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
########################################################################################################################### | |
# Podman # Node.js # MongoDB # MinIO | |
########################################################################################################################### | |
*************************************************************************************************************************** | |
# Node.js # node-mongodb-app:version1.0.0 | |
*************************************************************************************************************************** | |
=========================================================================================================================== | |
node-mongodb-app | |
├── index.js | |
├── public | |
│ ├── images | |
│ │ ├── MongoDB.png | |
│ │ └── Node.png | |
│ └── tachyons.min.css | |
└── views | |
└── index.pug | |
=========================================================================================================================== | |
% nano index.js | |
=========================================================================================================================== | |
const path = require('path') | |
const express = require('express') | |
const { MongoClient } = require('mongodb') | |
const multer = require('multer') | |
const { marked } = require('marked') | |
const app = express() | |
const port = process.env.PORT || 3000 | |
const mongoURL = process.env.MONGO_URL || 'mongodb://localhost:27017/dev' | |
const client = new MongoClient(mongoURL) | |
async function initMongo() { | |
console.log('Initialising MongoDB...') | |
console.log(`MongoDB URL: ${mongoURL}`) | |
let success = false | |
while (!success) { | |
try { | |
await client.connect() | |
success = true | |
} catch { | |
console.log('Error connecting to MongoDB, retrying in 1 second') | |
await new Promise(resolve => setTimeout(resolve, 1000)) | |
} | |
} | |
console.log('MongoDB initialised') | |
return client.db(client.s.options.dbName).collection('notes') | |
} | |
async function start() { | |
const db = await initMongo() | |
app.set('view engine', 'pug') | |
app.set('views', path.join(__dirname, 'views')) | |
app.use(express.static(path.join(__dirname, 'public'))) | |
app.use(express.static(path.join(__dirname, '/images'))) | |
app.get('/', async (req, res) => { | |
res.render('index', { notes: await retrieveNotes(db) }) | |
}) | |
app.post( | |
'/note', | |
multer({ dest: path.join(__dirname, 'public/uploads/') }).single('image'), | |
async (req, res) => { | |
if (!req.body.upload && req.body.description) { | |
await saveNote(db, { description: req.body.description }) | |
res.redirect('/') | |
} else if (req.body.upload && req.file) { | |
const link = `/uploads/${encodeURIComponent(req.file.filename)}` | |
res.render('index', { | |
content: `${req.body.description} `, | |
notes: await retrieveNotes(db), | |
}) | |
} | |
}, | |
) | |
app.listen(port, () => { | |
console.log(`App listening on http://localhost:${port}`) | |
}) | |
} | |
async function saveNote(db, note) { | |
await db.insertOne(note) | |
} | |
async function retrieveNotes(db) { | |
const notes = await db.find().toArray() | |
const sortedNotes = notes.reverse() | |
return sortedNotes.map(it => ({ ...it, description: marked(it.description) })) | |
} | |
start() | |
=========================================================================================================================== | |
% nano views/index.pug | |
=========================================================================================================================== | |
html | |
head | |
title= title | |
link(rel="stylesheet", href="tachyons.min.css") | |
body.ph3.pt0.pb4.mw7.center.sans-serif | |
h1.f2.mb0 #[img(src='/images/Node.png')] #[span.green N]ode.js #[img(src='/images/MongoDB.png')] #[span.green M]ongoDB | |
<!-- img(src="https://avatars.githubusercontent.com/u/9950313?s=48&v=4") --> | |
// img(src='https://avatars.githubusercontent.com/u/45120?s=48&v=4') | |
p.f5.mt1.mb4.lh-copy A Node.js-MongoDB app. | |
form(action="/note" method="POST" enctype="multipart/form-data") | |
ol.list.pl0 | |
li.mv3 | |
label.f6.b.db.mb2(for="image") Upload an image | |
input.f6.link.dim.br1.ba.b--black-20.ph3.pv2.mb2.dib.black.bg-white.pointer(type="file" name="image") | |
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer.ml2(type="submit" value="Upload" name="upload") | |
li.mv3 | |
label.f6.b.db.mb2(for="description") Write your content here | |
textarea.f4.db.border-box.hover-black.w-100.measure.ba.b--black-20.pa2.br2.mb2(rows="5" name="description") #{content || ''} | |
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer(type="submit" value="Publish" name="publish") | |
if notes.length > 0 | |
ul.list.pl0 | |
p.f6.b.db.mb2 Notes | |
each note in notes | |
li.mv3.bb.bw2.b--light-blue.bg-washed-blue.ph4.pv2 | |
p.measure!= note.description | |
else | |
p.lh-copy.f6 No notes! | |
=========================================================================================================================== | |
% curl -L https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css | |
% wget https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css --directory-prefix=public | |
% cat public/tachyons.min.css | |
% curl --output Node.png "https://avatars.githubusercontent.com/u/9950313?s=48&v=4" --create-dirs --output-dir public/images | |
% ls public/images/Node.png | |
% curl --output MongoDB.png "https://avatars.githubusercontent.com/u/45120?s=48&v=4" --create-dirs --output-dir public/images | |
% ls public/images/MongoDB.png | |
=========================================================================================================================== | |
[ | |
% ls node-mongodb-app/public/uploads | |
% rm -rf node-mongodb-app/public/uploads | |
] | |
% brew services start mongodb-community | |
% brew services stop mongodb-community | |
% brew services restart mongodb-community | |
% mongosh | |
> db.version() | |
> show dbs | |
> use dev | |
> show collections | |
[ | |
> db.notes.find() | |
] | |
> db.dropDatabase() | |
> use test | |
> show dbs | |
> exit | |
% node --version | |
% npm --version | |
% npm list --global | |
% npm update --global | |
% npm list --global | |
% npm init | |
[ | |
# package name: | |
node-mongodb-app-macos-arm64-v1 | |
# description | |
A Node.js-MongoDB app. | |
# keywords | |
Node.js, MongoDB | |
# author: | |
RajaniCode | |
# Is this OK? (yes) | |
yes | |
] | |
% npm install express | |
% npm install mongodb | |
% npm install marked | |
% npm install multer | |
% npm install pug | |
% npm list | |
% npm update | |
% npm list | |
% node index.js | |
<cotrol + C> | |
% nano Dockerfile | |
[ | |
FROM node:23.6.1-slim | |
COPY . . | |
RUN npm install | |
CMD [ "node", "index.js" ] | |
] | |
% cat Dockerfile | |
% echo node_modules > .dockerignore | |
% cat .dockerignore | |
% podman build -t node-mongodb-app-macos-arm64-v1 . | |
% podman container list | |
% podman image list --all | |
% podman tag localhost/node-mongodb-app-macos-arm64-v1 ghcr.io/rajanicode/node-mongodb-app:version1.0.0 | |
% podman image list --all | |
# https://github.com/settings/tokens | |
# Token Till Focus | |
% export CR_PAT=**************************************** | |
% echo $CR_PAT | podman login ghcr.io -u RajaniCode --password-stdin | |
[ | |
Login Succeeded! | |
] | |
% podman push ghcr.io/rajanicode/node-mongodb-app:version1.0.0 | |
[ | |
Writing manifest to image destination | |
] | |
https://github.com/users/RajaniCode/packages/container/package/node-mongodb-app | |
=========================================================================================================================== | |
*************************************************************************************************************************** | |
# Node.js # node-mongodb-app:version2.0.0 | |
*************************************************************************************************************************** | |
node-mongodb-app | |
├── index.js | |
├── public | |
│ ├── custom.style.css | |
│ ├── images | |
│ │ ├── Minio.png | |
│ │ ├── MongoDB.png | |
│ │ └── Node.png | |
│ └── tachyons.min.css | |
└── views | |
└── index.pug | |
=========================================================================================================================== | |
% nano index.js | |
=========================================================================================================================== | |
const path = require('path') | |
const express = require('express') | |
const { MongoClient } = require('mongodb') | |
const multer = require('multer') | |
const { marked } = require('marked') | |
// minio | |
const minio = require('minio') | |
const app = express() | |
const port = process.env.PORT || 3000 | |
const mongoURL = process.env.MONGO_URL || 'mongodb://localhost:27017/dev' | |
const client = new MongoClient(mongoURL) | |
// minio | |
const minioHost = process.env.MINIO_HOST || 'localhost' | |
// Destination bucket name | |
const minioBucket = minioHost + '-bucket' | |
const minioPort = process.env.MINIO_PORT || 9000 | |
const minioAccessKey = process.env.MINIO_ACCESS_KEY || 'minioadmin' | |
const minioSecretKey = process.env.MINIO_SECRET_KEY || 'minioadmin' | |
const useTLS = process.env.MINIO_TLS || 'false' | |
const useSSL = useTLS.toLowerCase() == 'true' ? true : false | |
async function initMongo() { | |
console.log('Initialising MongoDB...') | |
console.log(`MongoDB URL: ${mongoURL}`) | |
let success = false | |
while (!success) { | |
try { | |
await client.connect() | |
success = true | |
} catch { | |
console.log('Error connecting to MongoDB, retrying in 1 second') | |
await new Promise(resolve => setTimeout(resolve, 1000)) | |
} | |
} | |
console.log('MongoDB initialised') | |
return client.db(client.s.options.dbName).collection('notes') | |
} | |
// minio | |
async function initMinIO() { | |
console.log('Initialising MinIO...') | |
console.log(`Minio Bucket: ${minioBucket}`) | |
console.log(`Minio Host: ${minioHost}`) | |
console.log(`Minio Port: ${minioPort}`) | |
console.log(`Minio TLS: ${useSSL}`) | |
console.log(`Minio Access Key: ${minioAccessKey}`) | |
console.log(`Minio Secret Key: ${minioSecretKey}`) | |
const client = new minio.Client({ | |
endPoint: minioHost, | |
port: 9000, | |
useSSL: false, | |
accessKey: process.env.MINIO_ACCESS_KEY, | |
secretKey: process.env.MINIO_SECRET_KEY, | |
}) | |
let success = false | |
while (!success) { | |
try { | |
if (!(await client.bucketExists(minioBucket))) { | |
await client.makeBucket(minioBucket) | |
} | |
success = true | |
} catch { | |
await new Promise(resolve => setTimeout(resolve, 1000)) | |
} | |
} | |
console.log('MinIO initialised') | |
return client | |
} | |
async function start() { | |
const db = await initMongo() | |
// minio | |
const minio = await initMinIO() | |
app.set('view engine', 'pug') | |
app.set('views', path.join(__dirname, 'views')) | |
app.use(express.static(path.join(__dirname, 'public'))) | |
app.use(express.static(path.join(__dirname, '/images'))) | |
app.get('/', async (req, res) => { | |
res.render('index', { notes: await retrieveNotes(db) }) | |
}) | |
app.post( | |
'/note', | |
// minio | |
multer({ storage: multer.memoryStorage() }).single('image'), | |
async (req, res) => { | |
if (!req.body.upload && req.body.description) { | |
await saveNote(db, { description: req.body.description }) | |
res.redirect('/') | |
} else if (req.body.upload && req.file) { | |
// minio | |
await minio.putObject( | |
minioBucket, | |
req.file.originalname, | |
req.file.buffer, | |
) | |
// minio | |
const link = `/img/${encodeURIComponent(req.file.originalname)}` | |
res.render('index', { | |
content: `${req.body.description} `, | |
notes: await retrieveNotes(db), | |
}) | |
} | |
}, | |
) | |
// minio | |
app.get('/img/:name', async (req, res) => { | |
const stream = await minio.getObject( | |
minioBucket, | |
decodeURIComponent(req.params.name), | |
) | |
stream.pipe(res) | |
}) | |
app.listen(port, () => { | |
console.log(`App listening on http://localhost:${port}`) | |
}) | |
} | |
async function saveNote(db, note) { | |
await db.insertOne(note) | |
} | |
async function retrieveNotes(db) { | |
const notes = await db.find().toArray() | |
const sortedNotes = notes.reverse() | |
return sortedNotes.map(it => ({ ...it, description: marked(it.description) })) | |
} | |
start() | |
=========================================================================================================================== | |
% nano views/index.pug | |
=========================================================================================================================== | |
html | |
head | |
title= title | |
link(rel="stylesheet", href="tachyons.min.css") | |
link(rel="stylesheet", href="custom.style.css") | |
body.ph3.pt0.pb4.mw7.center.sans-serif | |
h1.f2.mb0 #[img(src='/images/Node.png')] #[span.green N]ode #[img(src='/images/MongoDB.png')] #[span.green M]ongoDB #[img(src='/images/Minio.png')] #[span.brick-red Minio] | |
<!-- img(src="https://avatars.githubusercontent.com/u/9950313?s=48&v=4") --> | |
// img(src='https://avatars.githubusercontent.com/u/45120?s=48&v=4') | |
// img(src='https://avatars.githubusercontent.com/u/695951?s=48&v=4') | |
p.f5.mt1.mb4.lh-copy A Node.js-MongoDB-MinIO app. | |
form(action="/note" method="POST" enctype="multipart/form-data") | |
ol.list.pl0 | |
li.mv3 | |
label.f6.b.db.mb2(for="image") Upload an image | |
input.f6.link.dim.br1.ba.b--black-20.ph3.pv2.mb2.dib.black.bg-white.pointer(type="file" name="image") | |
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer.ml2(type="submit" value="Upload" name="upload") | |
li.mv3 | |
label.f6.b.db.mb2(for="description") Write your content here | |
textarea.f4.db.border-box.hover-black.w-100.measure.ba.b--black-20.pa2.br2.mb2(rows="5" name="description") #{content || ''} | |
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer(type="submit" value="Publish" name="publish") | |
if notes.length > 0 | |
ul.list.pl0 | |
p.f6.b.db.mb2 Notes | |
each note in notes | |
li.mv3.bb.bw2.b--light-blue.bg-washed-blue.ph4.pv2 | |
p.measure!= note.description | |
else | |
p.lh-copy.f6 No notes! | |
=========================================================================================================================== | |
% curl -L https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css | |
% wget https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css --directory-prefix=public | |
% cat public/tachyons.min.css | |
% curl --output Node.png "https://avatars.githubusercontent.com/u/9950313?s=48&v=4" --create-dirs --output-dir public/images | |
% ls public/images/Node.png | |
% curl --output MongoDB.png "https://avatars.githubusercontent.com/u/45120?s=48&v=4" --create-dirs --output-dir public/images | |
% ls public/images/MongoDB.png | |
% curl --output Minio.png "https://avatars.githubusercontent.com/u/695951?s=48&v=4" --create-dirs --output-dir public/images | |
% ls public/images/Minio.png | |
=========================================================================================================================== | |
[ | |
% rm -rf ~/Minio | |
] | |
% minio server --address 127.0.0.1:9000 --console-address :9090 ~/Minio | |
% brew services start mongodb-community | |
% brew services stop mongodb-community | |
% brew services restart mongodb-community | |
% mongosh | |
> db.version() | |
> show dbs | |
> use dev | |
> show collections | |
[ | |
> db.notes.find() | |
] | |
> db.dropDatabase() | |
> use test | |
> show dbs | |
> exit | |
% node --version | |
% npm --version | |
% npm list --global | |
% npm update --global | |
% npm list --global | |
% npm init | |
# package name: | |
node-mongodb-app-macos-arm64-v2 | |
# description | |
A Node.js-MongoDB-MinIO app. | |
# keywords | |
Node.js, MongoDB, MinIO | |
# author: | |
RajaniCode | |
# Is this OK? (yes) | |
yes | |
% npm install express | |
% npm install mongodb | |
% npm install minio | |
% npm install marked | |
% npm install multer | |
% npm install pug | |
% npm list | |
% npm update | |
% npm list | |
% node index.js | |
<cotrol + C> | |
% nano Dockerfile | |
[ | |
FROM node:23.6.1-slim | |
COPY . . | |
RUN npm install | |
CMD [ "node", "index.js" ] | |
] | |
% cat Dockerfile | |
% echo node_modules > .dockerignore | |
% cat .dockerignore | |
% podman build -t node-mongodb-app-macos-arm64-v2 . | |
% podman container list | |
% podman image list --all | |
% podman tag localhost/node-mongodb-app-macos-arm64-v2 ghcr.io/rajanicode/node-mongodb-app:version2.0.0 | |
% podman image list --all | |
# https://github.com/settings/tokens | |
# Token Till Focus | |
% export CR_PAT=**************************************** | |
% echo $CR_PAT | podman login ghcr.io -u RajaniCode --password-stdin | |
[ | |
Login Succeeded! | |
] | |
% podman push ghcr.io/rajanicode/node-mongodb-app:version2.0.0 | |
[ | |
Writing manifest to image destination | |
] | |
https://github.com/users/RajaniCode/packages/container/package/node-mongodb-app | |
=========================================================================================================================== | |
*************************************************************************************************************************** | |
# https://github.com/settings/tokens | |
*************************************************************************************************************************** | |
# New personal access token (classic) | |
# Personal access tokens (classic) function like ordinary OAuth access tokens | |
# They can be used instead of a password for Git over HTTPS, or can be used to authenticate to the API over Basic Authentication. | |
# Note | |
<What’s this token for?> | |
# Expiration | |
<dd/mm/yyyy> | |
# Select scopes | |
<write:packagesUpload packages to GitHub Package Registry> | |
<read:packagesDownload packages from GitHub Package Registry> | |
<delete:packagesDelete packages from GitHub Package Registry> | |
*************************************************************************************************************************** | |
# Podman # run | |
*************************************************************************************************************************** | |
% podman --version | |
% podman version | |
% podman info | |
[ | |
% podman machine init | |
[ | |
% podman system connection list | |
] | |
% podman machine start | |
] | |
% podman container list --all | |
% podman image list --all | |
# https://github.com/settings/tokens | |
# Token Till Focus | |
% export CR_PAT=**************************************** | |
% echo $CR_PAT | podman login ghcr.io -u RajaniCode --password-stdin | |
[ | |
Login Succeeded! | |
] | |
# MinIO | |
% podman network create node-mongodb-app-network | |
% podman network list | |
% podman run \ | |
-p 9000:9000 \ | |
-p 9090:9090 \ | |
--name minio \ | |
--network=node-mongodb-app-network \ | |
-v Minio:/data \ | |
-e "MINIO_ACCESS_KEY=minioadmin" \ | |
-e "MINIO_SECRET_KEY=minioadmin" \ | |
ghcr.io/rajanicode/minio:version1.0.0 server /data --console-address ":9090" | |
# MongoDB | |
# Terminal New Window # podman run ghcr.io rajanicode mongo version1.0.0 | |
% podman network list | |
% podman run \ | |
-p 27017:27017 \ | |
--name=mongo \ | |
--rm \ | |
--network=node-mongodb-app-network \ | |
ghcr.io/rajanicode/mongo:version1.0.0 | |
# node-mongodb-app:version1.0.0 | |
# Terminal New Window | |
% podman network list | |
% podman run \ | |
--name=node-mongodb-app-v1 \ | |
--rm \ | |
--network=node-mongodb-app-network \ | |
-p 3001:3001 \ | |
-e PORT=3001 \ | |
-e MONGO_URL=mongodb://mongo:27017/dev \ | |
ghcr.io/rajanicode/node-mongodb-app:version1.0.0 | |
# node-mongodb-app:version2.0.0 | |
# Terminal New Window | |
% podman network list | |
% podman run \ | |
--name=node-mongodb-app-v2 \ | |
--rm \ | |
--network=node-mongodb-app-network \ | |
-p 3002:3002 \ | |
-e PORT=3002 \ | |
-e MONGO_URL=mongodb://mongo:27017/dev \ | |
-e "MINIO_ACCESS_KEY=minioadmin" \ | |
-e "MINIO_SECRET_KEY=minioadmin" \ | |
-e "MINIO_HOST=minio" \ | |
ghcr.io/rajanicode/node-mongodb-app:version2.0.0 | |
# mongosh # docker | |
# Terminal New Window | |
% podman exec -it mongo bash | |
$ cat /etc/os-release | |
$ arch | |
$ apt --version | |
$ apt-get --version | |
$ mongosh --version | |
$ mongosh | |
> db.version() | |
> show dbs | |
> use dev | |
> show collections | |
> db.notes.find() | |
[ | |
> db.dropDatabase() | |
> use test | |
> show dbs | |
] | |
> exit | |
podman machine init podman machine start | |
# mongosh # local machine | |
# Terminal New Window | |
% mongosh --version | |
% brew services start mongodb-community | |
% brew services stop mongodb-community | |
% brew services restart mongodb-community | |
> db.version() | |
> show dbs | |
> use dev | |
> show collections | |
> db.notes.find() | |
[ | |
> db.dropDatabase() | |
> use test | |
> show dbs | |
] | |
> exit | |
# mc # docker | |
# Terminal New Window | |
% podman exec -it minio bash | |
# cat /etc/os-release | |
# arch | |
# ls /etc | |
# ls /usr/bin | |
# mc --version | |
# mc ls data | |
# mc ls data/minio-bucket | |
# mc # local machine | |
# mc alias set # mc get | |
# Terminal New Window | |
% mc --version | |
# % mc alias set minio-podman-localhost "http://127.0.0.1:9000/" "minioadmin" "minioadmin" | |
% mc alias set minio-podman-docker "http://127.0.0.1:9000/" "minioadmin" "minioadmin" | |
% mc get minio-podman-docker/minio-bucket/logo_circle_podmandesktop.png $HOME/Desktop/DynamicFolder/new_name_podmandesktop.png | |
% mc get minio-podman-docker/minio-bucket/podman.io.png $HOME/Desktop/DynamicFolder | |
% mc get "minio-podman-docker/minio-bucket/GitHub Actions.png" "$HOME/Desktop/DynamicFolder/GitHub Actions.png" | |
*************************************************************************************************************************** | |
# Podman # Cleanup | |
*************************************************************************************************************************** | |
% podman system connection list | |
% podman machine list | |
% podman container list | |
% podman image list --all | |
[ | |
% podman ps --all --quiet | |
% podman stop $(podman ps -a -q) | |
% podman rm $(podman ps -a -q) --force | |
] | |
% podman container list --all --quiet | |
% podman stop $(podman container list -a -q) | |
% podman rm $(podman container list -a -q) --force | |
% podman container prune | |
% podman image list --all --quiet | |
% podman rmi $(podman image list -a -q) --force | |
% podman image prune --all | |
% podman volume list --quiet | |
% podman volume rm $(podman volume list --quiet) --force | |
% podman volume prune | |
% podman network ls | |
% podman network ls --quiet --filter dangling=true | |
% podman network rm $(podman network ls --quiet --filter dangling=true) | |
% podman network prune | |
% podman system info | |
% podman system prune --all --volumes | |
% podman system connection list | |
% podman machine list | |
% podman machine stop | |
% podman machine rm podman-machine-default | |
*************************************************************************************************************************** | |
# Podman | |
************************************************************************************************************************** | |
% brew install podman | |
[ | |
% brew uninstall podman | |
% ls /opt | |
% ls /opt/podman | |
% ls /opt/podman/bin | |
% ls /opt/podman/bin/podman | |
% sudo rm -rf /opt/podman | |
% ls /etc/paths.d | |
% sudo rm -rf /etc/paths.d/podman-pkg | |
% ls ~/.local | |
% rm -rf ~/.local | |
% ls ~/.config | |
% rm -rf ~/.config | |
] | |
*************************************************************************************************************************** | |
########################################################################################################################### | |
// Credits | |
/* | |
https://podman.io/ | |
https://nodejs.org/ | |
https://npmjs.com/ | |
https://expressjs.com/ | |
https://mongodb.com/ | |
https://min.io/ | |
https://ghcr.io/ | |
https://quay.io/ | |
https://docker.com/ | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment