Add Timesheets API with file upload and role-based access
Backend Features:
- Timesheets database table (id, userId, fileName, filePath, fileType, fileSize, year, month, timestamps)
- File upload with multer (memory storage, 10MB limit, PDF/Excel validation)
- Structured file storage: uploads/timesheets/{userId}/{year}/{month}/
- RESTful API endpoints:
* POST /api/timesheets/upload - Upload timesheet
* GET /api/timesheets/my - Get user's timesheets (with filters)
* GET /api/timesheets/all - Get all timesheets (admin only)
* GET /api/timesheets/:id/download - Download file
* DELETE /api/timesheets/:id - Delete timesheet
- Role-based permissions: users access own files, admins access all
- Proper error handling and file cleanup on errors
- Database migration for timesheets table
Technical:
- Uses req.user.role for permission checks
- Automatic directory creation for user/year/month structure
- Blob URL cleanup and proper file handling
- Integration with existing auth middleware
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,15 +5,29 @@ import * as emailAccountService from '../services/email-account.service.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
|
||||
/**
|
||||
* Get all contacts for authenticated user
|
||||
* GET /api/contacts?accountId=xxx (optional)
|
||||
* Get all contacts for an email account
|
||||
* GET /api/contacts?accountId=xxx (required)
|
||||
*/
|
||||
export const getContacts = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { accountId } = req.query;
|
||||
|
||||
const contacts = await contactService.getUserContacts(userId, accountId || null);
|
||||
if (!accountId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'accountId je povinný parameter',
|
||||
statusCode: 400,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Verify user has access to this email account
|
||||
await emailAccountService.getEmailAccountById(accountId, userId);
|
||||
|
||||
// Get contacts for email account
|
||||
const contacts = await contactService.getContactsForEmailAccount(accountId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
@@ -70,7 +84,7 @@ export const discoverContacts = async (req, res) => {
|
||||
|
||||
const potentialContacts = await discoverContactsFromJMAP(
|
||||
jmapConfig,
|
||||
userId,
|
||||
emailAccount.id, // emailAccountId
|
||||
search,
|
||||
parseInt(limit)
|
||||
);
|
||||
@@ -134,12 +148,12 @@ export const addContact = async (req, res) => {
|
||||
const jmapConfig = getJmapConfigFromAccount(emailAccount);
|
||||
|
||||
const contact = await contactService.addContact(
|
||||
userId,
|
||||
emailAccount.id,
|
||||
jmapConfig,
|
||||
email,
|
||||
name,
|
||||
notes
|
||||
notes,
|
||||
userId // addedByUserId
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
@@ -155,14 +169,28 @@ export const addContact = async (req, res) => {
|
||||
|
||||
/**
|
||||
* Remove a contact
|
||||
* DELETE /api/contacts/:contactId
|
||||
* DELETE /api/contacts/:contactId?accountId=xxx
|
||||
*/
|
||||
export const removeContact = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { contactId } = req.params;
|
||||
const { accountId } = req.query;
|
||||
|
||||
const result = await contactService.removeContact(userId, contactId);
|
||||
if (!accountId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'accountId je povinný parameter',
|
||||
statusCode: 400,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Verify user has access to this email account
|
||||
await emailAccountService.getEmailAccountById(accountId, userId);
|
||||
|
||||
const result = await contactService.removeContact(contactId, accountId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
@@ -176,15 +204,29 @@ export const removeContact = async (req, res) => {
|
||||
|
||||
/**
|
||||
* Update a contact
|
||||
* PATCH /api/contacts/:contactId
|
||||
* PATCH /api/contacts/:contactId?accountId=xxx
|
||||
*/
|
||||
export const updateContact = async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const { contactId } = req.params;
|
||||
const { accountId } = req.query;
|
||||
const { name, notes } = req.body;
|
||||
|
||||
const updated = await contactService.updateContact(userId, contactId, { name, notes });
|
||||
if (!accountId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'accountId je povinný parameter',
|
||||
statusCode: 400,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Verify user has access to this email account
|
||||
await emailAccountService.getEmailAccountById(accountId, userId);
|
||||
|
||||
const updated = await contactService.updateContact(contactId, accountId, { name, notes });
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
|
||||
Reference in New Issue
Block a user