Replace ?uid with /:uid (#482)

* Replace ?uid with /:uid

* fixup! Replace ?uid with /:uid
This commit is contained in:
Chris 2025-11-04 14:29:31 +02:00 committed by GitHub
parent eef7b98708
commit 8bc951b0ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 14 additions and 86 deletions

View file

@ -1342,93 +1342,24 @@ router.get('/tasks', async (req, res) => {
}
});
// GET /api/task?uid=...
router.get('/task', async (req, res) => {
try {
const { uid } = req.query;
if (_.isEmpty(uid)) {
return res
.status(400)
.json({ error: 'uid query parameter is required' });
}
const task = await Task.findOne({
where: {
uid: uid,
},
include: [
{
model: Tag,
attributes: ['id', 'name', 'uid'],
through: { attributes: [] },
},
{
model: Project,
attributes: ['id', 'name', 'uid'],
required: false,
},
{
model: Task,
as: 'Subtasks',
required: false,
include: [
{
model: Tag,
attributes: ['id', 'name', 'uid'],
through: { attributes: [] },
},
],
},
],
});
if (!task) {
return res.status(404).json({ error: 'Task not found.' });
}
// Ensure read access to the task
const access = await permissionsService.getAccess(
req.currentUser.id,
'task',
task.uid
);
if (access === 'none') {
return res.status(403).json({ error: 'Forbidden' });
}
const serializedTask = await serializeTask(
task,
req.currentUser.timezone,
{ skipDisplayNameTransform: true }
);
res.json(serializedTask);
} catch (error) {
logError('Error fetching task by UID:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// GET /api/task/:id
// GET /api/task/:uid - fetch task by UID only
router.get(
'/task/:id',
'/task/:uid',
hasAccess(
'ro',
'task',
async (req) => {
const t = await Task.findOne({
where: { id: req.params.id },
attributes: ['uid'],
});
return t?.uid;
// Return the UID directly for permission checking
return req.params.uid;
},
{ notFoundMessage: 'Task not found.' }
),
async (req, res) => {
try {
const { uid } = req.params;
const task = await Task.findOne({
where: { id: req.params.id },
where: { uid },
include: [
{
model: Tag,
@ -1443,6 +1374,7 @@ router.get(
{
model: Task,
as: 'Subtasks',
required: false,
include: [
{
model: Tag,

View file

@ -31,13 +31,13 @@ describe('Tasks Permissions', () => {
expect(res.body.error).toBe('Forbidden');
});
it("GET /api/task?uid=... should return 403 for other user's task", async () => {
it("GET /api/task/:uid should return 403 for other user's task", async () => {
const otherTask = await Task.create({
name: 'Other Task',
user_id: otherUser.id,
});
const res = await agent.get(`/api/task?uid=${otherTask.uid}`);
const res = await agent.get(`/api/task/${otherTask.uid}`);
expect(res.status).toBe(403);
expect(res.body.error).toBe('Forbidden');
});

View file

@ -286,9 +286,7 @@ describe('Recurring Task Display Fixes', () => {
priority: Task.PRIORITY.MEDIUM,
});
const response = await agent.get(
`/api/task?uid=${recurringTask.uid}`
);
const response = await agent.get(`/api/task/${recurringTask.uid}`);
expect(response.status).toBe(200);
expect(response.body.name).toBe('My Weekly Review');
@ -306,9 +304,7 @@ describe('Recurring Task Display Fixes', () => {
priority: Task.PRIORITY.MEDIUM,
});
const response = await agent.get(
`/api/task?uid=${monthlyTask.uid}`
);
const response = await agent.get(`/api/task/${monthlyTask.uid}`);
expect(response.status).toBe(200);
expect(response.body.name).toBe('Monthly Budget Review');

View file

@ -545,7 +545,7 @@ describe('Recurring Tasks API', () => {
});
it('should return recurring task with all recurrence fields', async () => {
const response = await agent.get(`/api/task/${recurringTask.id}`);
const response = await agent.get(`/api/task/${recurringTask.uid}`);
expect(response.status).toBe(200);
expect(response.body.name).toBe('Test Recurring Task');

View file

@ -100,7 +100,7 @@ export const fetchTaskById = async (taskId: number): Promise<Task> => {
};
export const fetchTaskByUid = async (uid: string): Promise<Task> => {
const response = await fetch(`/api/task?uid=${encodeURIComponent(uid)}`, {
const response = await fetch(`/api/task/${encodeURIComponent(uid)}`, {
credentials: 'include',
headers: getDefaultHeaders(),
});