feat(backend): invites, notifications, chat, uploads, teams, groups, hardening

- Pending workspace invites with accept/decline + bell notifications;
  invite permission extendable via workspace.invite group right
- Notifications API (list/unread, mark read, read-all) + realtime push
  to per-user socket rooms; chat messages notify other members
- Workspace team chat API (list/post/delete) + chat:join socket rooms
- Image uploads (multer, 5MB image-only) served from /uploads
- Teams (managers/members), team-owned workspaces (TEAM badge), team
  task boards (team_id) and task assignees (workspace members only)
- Permission groups with teams.create/edit/invite + workspace.invite,
  default Team Creators seeding, hasPerm/canManageTeam guards
- Public registration disabled after install; admins create users
  (pending invites auto-link); users gain bg_glass setting
- Crash fix: express-async-errors + 400 on malformed IDs instead of
  unhandled rejections killing the server
This commit is contained in:
x3
2026-09-22 14:27:33 +02:00
parent fb396d313a
commit c3c77333db
18 changed files with 934 additions and 36 deletions
+16 -1
View File
@@ -1,4 +1,5 @@
import 'dotenv/config';
import 'express-async-errors';
import http from 'node:http';
import express from 'express';
import cors from 'cors';
@@ -15,6 +16,12 @@ import bookRoutes from './routes/books.js';
import taskRoutes from './routes/tasks.js';
import timeRoutes from './routes/time.js';
import adminRoutes from './routes/admin.js';
import inviteRoutes from './routes/invites.js';
import notificationRoutes from './routes/notifications.js';
import chatRoutes from './routes/chat.js';
import teamRoutes from './routes/teams.js';
import groupRoutes from './routes/groups.js';
import uploadRoutes from './routes/upload.js';
import { initSocket } from './socket.js';
const app = express();
@@ -51,6 +58,13 @@ app.use('/api/books', bookRoutes);
app.use('/api/tasks', taskRoutes);
app.use('/api/time', timeRoutes);
app.use('/api/admin', adminRoutes);
app.use('/api/invites', inviteRoutes);
app.use('/api/notifications', notificationRoutes);
app.use('/api/chat', chatRoutes);
app.use('/api/teams', teamRoutes);
app.use('/api/groups', groupRoutes);
app.use('/api/upload', uploadRoutes);
app.use('/uploads', express.static('uploads'));
app.get('/api/health', async (_req, res) => {
try {
@@ -62,7 +76,8 @@ app.get('/api/health', async (_req, res) => {
});
app.use((err, _req, res, _next) => {
console.error('[unhandled]', err);
console.error('[unhandled]', err?.message || err);
if (err?.code === '22P02') return res.status(400).json({ error: 'Invalid ID format' });
res.status(500).json({ error: 'Internal server error' });
});