vimalk78 commited on
Commit
88452f7
·
1 Parent(s): 245c727

Dockerized

Browse files

Signed-off-by: Vimal Kumar <[email protected]>

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
- DATABASE_URL=postgresql://user:pass@host:port/db
185
- CORS_ORIGIN=https://your-frontend-domain.com
186
- JWT_SECRET=your-secure-secret
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 || 3000;
9
 
10
  app.use(helmet());
11
 
12
- const corsOptions = {
13
- origin: process.env.CORS_ORIGIN || ['http://localhost:5173', 'http://localhost:3000'],
14
- credentials: true,
15
- optionsSuccessStatus: 200
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
- app.get('/', (req, res) => {
46
- res.json({
47
- message: 'Crossword Puzzle API',
48
- version: '1.0.0',
49
- endpoints: {
50
- topics: 'GET /api/topics',
51
- generate: 'POST /api/generate',
52
- validate: 'POST /api/validate',
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
- console.log(`CORS enabled for: ${JSON.stringify(corsOptions.origin)}`);
 
 
 
 
 
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 {