Initial commit — Electron + React frontend, Express/Docker backend, members & project management

This commit is contained in:
Ryan Lancaster
2026-03-15 13:40:01 -07:00
commit a3949c32ee
24 changed files with 12583 additions and 0 deletions

2
backend/.dockerignore Normal file
View File

@@ -0,0 +1,2 @@
node_modules
npm-debug.log

12
backend/Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]

106
backend/index.js Normal file
View File

@@ -0,0 +1,106 @@
import express from 'express';
import cors from 'cors';
import { Low } from 'lowdb';
import { JSONFile } from 'lowdb/node';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const dataDir = join(__dirname, 'data');
import { mkdirSync } from 'fs';
mkdirSync(dataDir, { recursive: true });
const dbFile = join(dataDir, 'db.json');
const adapter = new JSONFile(dbFile);
const defaultData = { members: [], projects: [], tasks: [] };
const db = new Low(adapter, defaultData);
await db.read();
await db.write();
const app = express();
app.use(cors());
app.use(express.json());
// Member CRUD
app.get('/api/members', (req, res) => {
res.json(db.data.members);
});
app.post('/api/members', (req, res) => {
const member = { id: Date.now().toString(), ...req.body };
db.data.members.push(member);
db.write();
res.status(201).json(member);
});
app.delete('/api/members/:id', (req, res) => {
db.data.members = db.data.members.filter(m => m.id !== req.params.id);
db.write();
res.status(204).end();
});
app.put('/api/members/:id', async (req, res) => {
const idx = db.data.members.findIndex(m => m.id === req.params.id);
if (idx === -1) return res.status(404).json({ error: 'Member not found' });
db.data.members[idx] = { ...db.data.members[idx], ...req.body, id: req.params.id };
await db.write();
res.json(db.data.members[idx]);
});
// Project CRUD
app.get('/api/projects', (req, res) => {
res.json(db.data.projects);
});
app.post('/api/projects', (req, res) => {
const project = { id: Date.now().toString(), members: [], ...req.body };
db.data.projects.push(project);
db.write();
res.status(201).json(project);
});
app.put('/api/projects/:id', async (req, res) => {
const idx = db.data.projects.findIndex(p => p.id === req.params.id);
const updated = { ...req.body, id: req.params.id };
if (idx === -1) db.data.projects.push(updated);
else db.data.projects[idx] = updated;
await db.write();
res.json(updated);
});
app.delete('/api/projects/:id', (req, res) => {
db.data.projects = db.data.projects.filter(p => p.id !== req.params.id);
db.write();
res.status(204).end();
});
// Assign member to project
app.post('/api/projects/:projectId/members/:memberId', (req, res) => {
const project = db.data.projects.find(p => p.id === req.params.projectId);
if (!project) return res.status(404).json({ error: 'Project not found' });
if (!project.members.includes(req.params.memberId)) {
project.members.push(req.params.memberId);
db.write();
}
res.json(project);
});
// Tasks (assign to member in project)
app.post('/api/projects/:projectId/tasks', (req, res) => {
const { title, description, memberId } = req.body;
const project = db.data.projects.find(p => p.id === req.params.projectId);
if (!project) return res.status(404).json({ error: 'Project not found' });
if (!project.members.includes(memberId)) return res.status(400).json({ error: 'Member not assigned to project' });
const task = { id: Date.now().toString(), projectId: project.id, memberId, title, description, status: 'todo' };
db.data.tasks.push(task);
db.write();
res.status(201).json(task);
});
app.get('/api/projects/:projectId/tasks', (req, res) => {
const tasks = db.data.tasks.filter(t => t.projectId === req.params.projectId);
res.json(tasks);
});
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => console.log(`Backend running on port ${PORT}`));

1259
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
backend/package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "project-manager-backend",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
},
"dependencies": {
"express": "^4.18.2",
"lowdb": "^6.0.1",
"cors": "^2.8.5"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}