feat: add Dockerfile and .dockerignore, refactor backend to use DATA_DIR for database and uploads
Now able to use docker image
This commit is contained in:
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
node_modules
|
||||||
|
frontend/node_modules
|
||||||
|
backend/node_modules
|
||||||
|
frontend/dist
|
||||||
|
backend/dist
|
||||||
|
backend/db.sqlite
|
||||||
|
backend/uploads
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -8,26 +8,19 @@ dist/
|
|||||||
frontend/dist/
|
frontend/dist/
|
||||||
backend/dist/
|
backend/dist/
|
||||||
|
|
||||||
# SQLite database and uploads
|
# Persistent backend data (database and uploads)
|
||||||
backend/db.sqlite
|
backend/data/
|
||||||
backend/uploads/
|
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs/
|
logs/
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
pnpm-debug.log*
|
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
# Environment files (if any)
|
# Environment files (if any)
|
||||||
.env
|
.env
|
||||||
|
|||||||
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Build frontend
|
||||||
|
FROM node:24-alpine AS frontend
|
||||||
|
WORKDIR /app/frontend
|
||||||
|
COPY frontend/package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY frontend ./
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Build backend
|
||||||
|
FROM node:24-alpine AS backend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY backend/package*.json ./backend/
|
||||||
|
COPY backend/ ./backend/
|
||||||
|
RUN cd backend && npm install
|
||||||
|
|
||||||
|
RUN mkdir -p /data/uploads && chmod -R 777 /data
|
||||||
|
|
||||||
|
# Copy built frontend into backend
|
||||||
|
COPY --from=frontend /app/frontend/dist ./frontend/dist
|
||||||
|
|
||||||
|
WORKDIR /app/backend
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV DATA_DIR=/data
|
||||||
|
|
||||||
|
CMD ["node", "index.js"]
|
||||||
@@ -6,13 +6,19 @@ const multer = require('multer');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const dbPath = path.join(__dirname, 'db.sqlite');
|
|
||||||
const db = new sqlite3.Database(dbPath);
|
|
||||||
const upload = multer({ dest: path.join(__dirname, 'uploads/') });
|
|
||||||
|
|
||||||
|
// Use DATA_DIR env var if set, otherwise use local "data" folder
|
||||||
|
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, 'data');
|
||||||
|
|
||||||
|
const dbPath = path.join(DATA_DIR, 'db.sqlite');
|
||||||
|
const db = new sqlite3.Database(dbPath);
|
||||||
|
const upload = multer({ dest: path.join(DATA_DIR, 'uploads/') });
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
app.use('/uploads', express.static(path.join(DATA_DIR, 'uploads')));
|
||||||
|
|
||||||
|
// Create uploads directory if it doesn't exist
|
||||||
|
fs.mkdirSync(path.join(DATA_DIR, 'uploads'), { recursive: true });
|
||||||
|
|
||||||
// Initialize database and ensure counters table exists
|
// Initialize database and ensure counters table exists
|
||||||
db.serialize(() => {
|
db.serialize(() => {
|
||||||
@@ -38,7 +44,6 @@ app.get('/api/counters', (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Create a new counter with image
|
// Create a new counter with image
|
||||||
app.post('/api/counters', upload.single('image'), (req, res) => {
|
app.post('/api/counters', upload.single('image'), (req, res) => {
|
||||||
const { name = 'Counter', value = 0 } = req.body;
|
const { name = 'Counter', value = 0 } = req.body;
|
||||||
@@ -97,12 +102,20 @@ const clientBuildPath = path.join(__dirname, '..', 'frontend', 'dist');
|
|||||||
app.use(express.static(clientBuildPath));
|
app.use(express.static(clientBuildPath));
|
||||||
|
|
||||||
// For SPA: serve index.html for any unknown route (after API and uploads)
|
// For SPA: serve index.html for any unknown route (after API and uploads)
|
||||||
// app.get('*', (req, res) => {
|
app.get('/*path', (req, res) => {
|
||||||
// if (req.path.startsWith('/api') || req.path.startsWith('/uploads')) return res.status(404).end();
|
if (req.path.startsWith('/api') || req.path.startsWith('/uploads')) return res.status(404).end();
|
||||||
// res.sendFile(path.join(clientBuildPath, 'index.html'));
|
res.sendFile(path.join(clientBuildPath, 'index.html'));
|
||||||
// });
|
});
|
||||||
|
|
||||||
const PORT = 3000;
|
const PORT = 3000;
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`API server running on http://localhost:${PORT}`);
|
console.log(`API server running on http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
console.log('SIGTERM received, shutting down gracefully...');
|
||||||
|
db.close(() => {
|
||||||
|
console.log('Database connection closed.');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user