Dockerized
Browse filesSigned-off-by: Vimal Kumar <[email protected]>
- README.md +21 -1
- crossword-app/.dockerignore +67 -0
- crossword-app/Dockerfile +36 -0
- crossword-app/README.md +49 -3
- crossword-app/backend/package.json +2 -0
- crossword-app/backend/src/app.js +50 -19
- crossword-app/frontend/src/hooks/useCrossword.js +1 -1
README.md
CHANGED
@@ -37,6 +37,8 @@ A full-stack web application for generating and solving crossword puzzles with v
|
|
37 |
|
38 |
## Running the Application
|
39 |
|
|
|
|
|
40 |
1. **Start the backend server**
|
41 |
```bash
|
42 |
cd crossword-app/backend
|
@@ -53,6 +55,19 @@ A full-stack web application for generating and solving crossword puzzles with v
|
|
53 |
|
54 |
3. **Open your browser** and navigate to `http://localhost:5173`
|
55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
## How to Use
|
57 |
|
58 |
1. Select one or more topics from the available options
|
@@ -82,4 +97,9 @@ cross-words/
|
|
82 |
|
83 |
- **Frontend**: React, Vite, CSS
|
84 |
- **Backend**: Node.js, Express
|
85 |
-
- **Algorithm**: Backtracking-based crossword generation
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
## Running the Application
|
39 |
|
40 |
+
### Development Mode
|
41 |
+
|
42 |
1. **Start the backend server**
|
43 |
```bash
|
44 |
cd crossword-app/backend
|
|
|
55 |
|
56 |
3. **Open your browser** and navigate to `http://localhost:5173`
|
57 |
|
58 |
+
### Docker Deployment
|
59 |
+
|
60 |
+
**Build and run with Docker:**
|
61 |
+
```bash
|
62 |
+
cd crossword-app
|
63 |
+
docker build -t crossword-app .
|
64 |
+
docker run -p 7860:7860 -e NODE_ENV=production crossword-app
|
65 |
+
```
|
66 |
+
|
67 |
+
Open `http://localhost:7860` to access the application.
|
68 |
+
|
69 |
+
**For Hugging Face Spaces:** Upload the `crossword-app/` directory to a new Docker Space.
|
70 |
+
|
71 |
## How to Use
|
72 |
|
73 |
1. Select one or more topics from the available options
|
|
|
97 |
|
98 |
- **Frontend**: React, Vite, CSS
|
99 |
- **Backend**: Node.js, Express
|
100 |
+
- **Algorithm**: Backtracking-based crossword generation
|
101 |
+
- **Deployment**: Docker (ready for Hugging Face Spaces)
|
102 |
+
|
103 |
+
## Documentation
|
104 |
+
|
105 |
+
For detailed technical documentation, deployment guides, and API specifications, see the [crossword-app README](./crossword-app/README.md).
|
crossword-app/.dockerignore
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Node modules
|
2 |
+
**/node_modules
|
3 |
+
**/npm-debug.log*
|
4 |
+
**/yarn-debug.log*
|
5 |
+
**/yarn-error.log*
|
6 |
+
|
7 |
+
# Build directories
|
8 |
+
frontend/dist
|
9 |
+
backend/public
|
10 |
+
|
11 |
+
# Development files
|
12 |
+
**/.git
|
13 |
+
**/.gitignore
|
14 |
+
**/.env.local
|
15 |
+
**/.env.development
|
16 |
+
**/.env.test
|
17 |
+
**/.vscode
|
18 |
+
**/.idea
|
19 |
+
|
20 |
+
# OS generated files
|
21 |
+
.DS_Store
|
22 |
+
.DS_Store?
|
23 |
+
._*
|
24 |
+
.Spotlight-V100
|
25 |
+
.Trashes
|
26 |
+
ehthumbs.db
|
27 |
+
Thumbs.db
|
28 |
+
|
29 |
+
# Logs
|
30 |
+
**/logs
|
31 |
+
**/*.log
|
32 |
+
|
33 |
+
# Runtime data
|
34 |
+
**/pids
|
35 |
+
**/*.pid
|
36 |
+
**/*.seed
|
37 |
+
**/*.pid.lock
|
38 |
+
|
39 |
+
# Coverage directory used by tools like istanbul
|
40 |
+
**/coverage
|
41 |
+
**/.nyc_output
|
42 |
+
|
43 |
+
# Optional npm cache directory
|
44 |
+
**/.npm
|
45 |
+
|
46 |
+
# Optional eslint cache
|
47 |
+
**/.eslintcache
|
48 |
+
|
49 |
+
# Documentation
|
50 |
+
**/docs
|
51 |
+
**/README.md
|
52 |
+
**/samples
|
53 |
+
**/hack
|
54 |
+
|
55 |
+
# Test files
|
56 |
+
**/*.test.js
|
57 |
+
**/*.spec.js
|
58 |
+
**/test
|
59 |
+
|
60 |
+
# Development only files
|
61 |
+
**/nodemon.json
|
62 |
+
**/.prettierrc
|
63 |
+
**/.eslintrc*
|
64 |
+
|
65 |
+
# Python virtual environment (if any)
|
66 |
+
**/venv
|
67 |
+
**/__pycache__
|
crossword-app/Dockerfile
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use Node.js 18 as base image
|
2 |
+
FROM node:18-alpine
|
3 |
+
|
4 |
+
# Set working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy package files for both frontend and backend
|
8 |
+
COPY frontend/package*.json ./frontend/
|
9 |
+
COPY backend/package*.json ./backend/
|
10 |
+
|
11 |
+
# Install dependencies for both frontend and backend
|
12 |
+
RUN cd frontend && npm ci
|
13 |
+
RUN cd backend && npm ci --only=production
|
14 |
+
|
15 |
+
# Copy source code
|
16 |
+
COPY frontend/ ./frontend/
|
17 |
+
COPY backend/ ./backend/
|
18 |
+
|
19 |
+
# Build the React frontend
|
20 |
+
RUN cd frontend && npm run build
|
21 |
+
|
22 |
+
# Copy built frontend files to backend public directory
|
23 |
+
RUN mkdir -p backend/public && cp -r frontend/dist/* backend/public/
|
24 |
+
|
25 |
+
# Set working directory to backend for runtime
|
26 |
+
WORKDIR /app/backend
|
27 |
+
|
28 |
+
# Expose port 7860 (Hugging Face Spaces standard)
|
29 |
+
EXPOSE 7860
|
30 |
+
|
31 |
+
# Set environment to production
|
32 |
+
ENV NODE_ENV=production
|
33 |
+
ENV PORT=7860
|
34 |
+
|
35 |
+
# Start the backend server
|
36 |
+
CMD ["npm", "start"]
|
crossword-app/README.md
CHANGED
@@ -1,3 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# Crossword Puzzle Generator
|
2 |
|
3 |
A full-stack web application that generates custom crossword puzzles based on selected topics using React frontend and Node.js backend.
|
@@ -164,6 +174,42 @@ curl -X POST http://localhost:3000/api/generate \
|
|
164 |
|
165 |
## Deployment
|
166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
### Frontend (Vercel/Netlify)
|
168 |
```bash
|
169 |
cd frontend
|
@@ -181,9 +227,9 @@ cd backend
|
|
181 |
### Environment Variables for Production
|
182 |
```bash
|
183 |
NODE_ENV=production
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
```
|
188 |
|
189 |
## Contributing
|
|
|
1 |
+
---
|
2 |
+
title: Crossword Puzzle Generator
|
3 |
+
emoji: 🧩
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: purple
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
license: mit
|
9 |
+
---
|
10 |
+
|
11 |
# Crossword Puzzle Generator
|
12 |
|
13 |
A full-stack web application that generates custom crossword puzzles based on selected topics using React frontend and Node.js backend.
|
|
|
174 |
|
175 |
## Deployment
|
176 |
|
177 |
+
### Docker (Hugging Face Spaces/Local)
|
178 |
+
|
179 |
+
**Build and run locally:**
|
180 |
+
```bash
|
181 |
+
# Build the Docker image
|
182 |
+
docker build -t crossword-app .
|
183 |
+
|
184 |
+
# Run the container (production mode)
|
185 |
+
docker run -p 7860:7860 -e NODE_ENV=production crossword-app
|
186 |
+
|
187 |
+
# Or run in background
|
188 |
+
docker run -d -p 7860:7860 -e NODE_ENV=production --name crossword-app crossword-app
|
189 |
+
```
|
190 |
+
|
191 |
+
**Test the deployment:**
|
192 |
+
```bash
|
193 |
+
# Test API endpoint
|
194 |
+
curl http://localhost:7860/api/topics
|
195 |
+
|
196 |
+
# Test frontend (should return HTML)
|
197 |
+
curl http://localhost:7860/
|
198 |
+
|
199 |
+
# View container logs
|
200 |
+
docker logs crossword-app
|
201 |
+
|
202 |
+
# Stop and cleanup
|
203 |
+
docker stop crossword-app && docker rm crossword-app
|
204 |
+
```
|
205 |
+
|
206 |
+
The app will be available at http://localhost:7860
|
207 |
+
|
208 |
+
**For Hugging Face Spaces:**
|
209 |
+
1. Create a new Space with "Docker" SDK
|
210 |
+
2. Upload the entire `crossword-app/` directory
|
211 |
+
3. The container will build automatically and deploy on port 7860
|
212 |
+
|
213 |
### Frontend (Vercel/Netlify)
|
214 |
```bash
|
215 |
cd frontend
|
|
|
227 |
### Environment Variables for Production
|
228 |
```bash
|
229 |
NODE_ENV=production
|
230 |
+
PORT=7860
|
231 |
+
DATABASE_URL=postgresql://user:pass@host:port/db (optional)
|
232 |
+
CORS_ORIGIN=https://your-frontend-domain.com (for separate deployments)
|
233 |
```
|
234 |
|
235 |
## Contributing
|
crossword-app/backend/package.json
CHANGED
@@ -7,6 +7,8 @@
|
|
7 |
"scripts": {
|
8 |
"start": "node src/app.js",
|
9 |
"dev": "nodemon src/app.js",
|
|
|
|
|
10 |
"test": "jest",
|
11 |
"test:watch": "jest --watch",
|
12 |
"lint": "eslint src/",
|
|
|
7 |
"scripts": {
|
8 |
"start": "node src/app.js",
|
9 |
"dev": "nodemon src/app.js",
|
10 |
+
"build:frontend": "cd ../frontend && npm run build",
|
11 |
+
"build": "npm run build:frontend && mkdir -p public && cp -r ../frontend/dist/* public/",
|
12 |
"test": "jest",
|
13 |
"test:watch": "jest --watch",
|
14 |
"lint": "eslint src/",
|
crossword-app/backend/src/app.js
CHANGED
@@ -2,18 +2,31 @@ const express = require('express');
|
|
2 |
const cors = require('cors');
|
3 |
const helmet = require('helmet');
|
4 |
const rateLimit = require('express-rate-limit');
|
|
|
5 |
const apiRoutes = require('./routes/api');
|
6 |
|
7 |
const app = express();
|
8 |
-
const PORT = process.env.PORT ||
|
9 |
|
10 |
app.use(helmet());
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
app.use(cors(corsOptions));
|
18 |
|
19 |
const limiter = rateLimit({
|
@@ -42,19 +55,32 @@ app.use((req, res, next) => {
|
|
42 |
app.use('/api/generate', generateLimiter);
|
43 |
app.use('/api', apiRoutes);
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
words: 'GET /api/words/:topic',
|
54 |
-
health: 'GET /api/health'
|
55 |
-
}
|
56 |
});
|
57 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
app.use((req, res) => {
|
60 |
res.status(404).json({
|
@@ -87,7 +113,12 @@ if (require.main === module) {
|
|
87 |
app.listen(PORT, () => {
|
88 |
console.log(`Server running on port ${PORT}`);
|
89 |
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
91 |
});
|
92 |
}
|
93 |
|
|
|
2 |
const cors = require('cors');
|
3 |
const helmet = require('helmet');
|
4 |
const rateLimit = require('express-rate-limit');
|
5 |
+
const path = require('path');
|
6 |
const apiRoutes = require('./routes/api');
|
7 |
|
8 |
const app = express();
|
9 |
+
const PORT = process.env.PORT || 7860;
|
10 |
|
11 |
app.use(helmet());
|
12 |
|
13 |
+
// CORS configuration
|
14 |
+
let corsOptions;
|
15 |
+
if (process.env.NODE_ENV === 'production') {
|
16 |
+
// In production, allow same origin (since frontend is served from same server)
|
17 |
+
corsOptions = {
|
18 |
+
origin: true,
|
19 |
+
credentials: true,
|
20 |
+
optionsSuccessStatus: 200
|
21 |
+
};
|
22 |
+
} else {
|
23 |
+
// Development mode - allow dev servers
|
24 |
+
corsOptions = {
|
25 |
+
origin: process.env.CORS_ORIGIN || ['http://localhost:5173', 'http://localhost:3000'],
|
26 |
+
credentials: true,
|
27 |
+
optionsSuccessStatus: 200
|
28 |
+
};
|
29 |
+
}
|
30 |
app.use(cors(corsOptions));
|
31 |
|
32 |
const limiter = rateLimit({
|
|
|
55 |
app.use('/api/generate', generateLimiter);
|
56 |
app.use('/api', apiRoutes);
|
57 |
|
58 |
+
// Serve static files in production
|
59 |
+
if (process.env.NODE_ENV === 'production') {
|
60 |
+
const staticPath = path.join(__dirname, '../public');
|
61 |
+
app.use(express.static(staticPath));
|
62 |
+
|
63 |
+
// Handle React Router routes - serve index.html for non-API routes
|
64 |
+
app.get('*', (req, res) => {
|
65 |
+
res.sendFile(path.join(staticPath, 'index.html'));
|
|
|
|
|
|
|
66 |
});
|
67 |
+
} else {
|
68 |
+
// Development mode - show API info
|
69 |
+
app.get('/', (req, res) => {
|
70 |
+
res.json({
|
71 |
+
message: 'Crossword Puzzle API',
|
72 |
+
version: '1.0.0',
|
73 |
+
mode: 'development',
|
74 |
+
endpoints: {
|
75 |
+
topics: 'GET /api/topics',
|
76 |
+
generate: 'POST /api/generate',
|
77 |
+
validate: 'POST /api/validate',
|
78 |
+
words: 'GET /api/words/:topic',
|
79 |
+
health: 'GET /api/health'
|
80 |
+
}
|
81 |
+
});
|
82 |
+
});
|
83 |
+
}
|
84 |
|
85 |
app.use((req, res) => {
|
86 |
res.status(404).json({
|
|
|
113 |
app.listen(PORT, () => {
|
114 |
console.log(`Server running on port ${PORT}`);
|
115 |
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
116 |
+
if (process.env.NODE_ENV === 'production') {
|
117 |
+
console.log(`Serving React app from /public directory`);
|
118 |
+
console.log(`CORS enabled for same origin`);
|
119 |
+
} else {
|
120 |
+
console.log(`CORS enabled for: ${JSON.stringify(corsOptions.origin)}`);
|
121 |
+
}
|
122 |
});
|
123 |
}
|
124 |
|
crossword-app/frontend/src/hooks/useCrossword.js
CHANGED
@@ -6,7 +6,7 @@ const useCrossword = () => {
|
|
6 |
const [error, setError] = useState(null);
|
7 |
const [topics, setTopics] = useState([]);
|
8 |
|
9 |
-
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
|
10 |
|
11 |
const fetchTopics = useCallback(async () => {
|
12 |
try {
|
|
|
6 |
const [error, setError] = useState(null);
|
7 |
const [topics, setTopics] = useState([]);
|
8 |
|
9 |
+
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || (import.meta.env.PROD ? '' : 'http://localhost:3000');
|
10 |
|
11 |
const fetchTopics = useCallback(async () => {
|
12 |
try {
|