fix: Adapt SQL queries to actual Outline database schema
- Users: Use role enum instead of isAdmin/isViewer/isSuspended booleans - Users: Remove non-existent username column - Groups: Fix group_users table (no deletedAt, composite PK) - Attachments: Remove url and deletedAt columns, use hard delete All 10/10 core queries now pass validation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -46,7 +46,7 @@ const listAttachments: BaseTool<AttachmentListArgs> = {
|
||||
},
|
||||
handler: async (args, pgClient): Promise<ToolResponse> => {
|
||||
const { limit, offset } = validatePagination(args.limit, args.offset);
|
||||
const conditions: string[] = ['a."deletedAt" IS NULL'];
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
@@ -74,13 +74,12 @@ const listAttachments: BaseTool<AttachmentListArgs> = {
|
||||
params.push(args.team_id);
|
||||
}
|
||||
|
||||
const whereClause = `WHERE ${conditions.join(' AND ')}`;
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
a.id,
|
||||
a.key,
|
||||
a.url,
|
||||
a."contentType",
|
||||
a.size,
|
||||
a.acl,
|
||||
@@ -89,6 +88,8 @@ const listAttachments: BaseTool<AttachmentListArgs> = {
|
||||
a."teamId",
|
||||
a."createdAt",
|
||||
a."updatedAt",
|
||||
a."lastAccessedAt",
|
||||
a."expiresAt",
|
||||
d.title as "documentTitle",
|
||||
u.name as "uploadedByName",
|
||||
u.email as "uploadedByEmail"
|
||||
@@ -151,7 +152,6 @@ const getAttachment: BaseTool<GetAttachmentArgs> = {
|
||||
SELECT
|
||||
a.id,
|
||||
a.key,
|
||||
a.url,
|
||||
a."contentType",
|
||||
a.size,
|
||||
a.acl,
|
||||
@@ -160,7 +160,8 @@ const getAttachment: BaseTool<GetAttachmentArgs> = {
|
||||
a."teamId",
|
||||
a."createdAt",
|
||||
a."updatedAt",
|
||||
a."deletedAt",
|
||||
a."lastAccessedAt",
|
||||
a."expiresAt",
|
||||
d.title as "documentTitle",
|
||||
d."collectionId",
|
||||
u.name as "uploadedByName",
|
||||
@@ -243,7 +244,7 @@ const createAttachment: BaseTool<CreateAttachmentArgs> = {
|
||||
|
||||
// Get first admin user and team
|
||||
const userQuery = await pgClient.query(
|
||||
'SELECT u.id, u."teamId" FROM users u WHERE u."isAdmin" = true AND u."deletedAt" IS NULL LIMIT 1'
|
||||
"SELECT u.id, u.\"teamId\" FROM users u WHERE u.role = 'admin' AND u.\"deletedAt\" IS NULL LIMIT 1"
|
||||
);
|
||||
|
||||
if (userQuery.rows.length === 0) {
|
||||
@@ -253,14 +254,13 @@ const createAttachment: BaseTool<CreateAttachmentArgs> = {
|
||||
const userId = userQuery.rows[0].id;
|
||||
const teamId = userQuery.rows[0].teamId;
|
||||
|
||||
// Generate URL and key (in real implementation, this would be S3/storage URL)
|
||||
// Generate key (path in storage)
|
||||
const key = `attachments/${Date.now()}-${args.name}`;
|
||||
const url = `/api/attachments.redirect?id=PLACEHOLDER`;
|
||||
|
||||
const query = `
|
||||
INSERT INTO attachments (
|
||||
id,
|
||||
key,
|
||||
url,
|
||||
"contentType",
|
||||
size,
|
||||
acl,
|
||||
@@ -269,13 +269,12 @@ const createAttachment: BaseTool<CreateAttachmentArgs> = {
|
||||
"teamId",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
|
||||
) VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
const result = await pgClient.query(query, [
|
||||
key,
|
||||
url,
|
||||
args.content_type,
|
||||
args.size,
|
||||
'private', // Default ACL
|
||||
@@ -306,7 +305,7 @@ const createAttachment: BaseTool<CreateAttachmentArgs> = {
|
||||
*/
|
||||
const deleteAttachment: BaseTool<GetAttachmentArgs> = {
|
||||
name: 'outline_attachments_delete',
|
||||
description: 'Soft delete an attachment. The attachment record is marked as deleted but not removed from the database.',
|
||||
description: 'Delete an attachment permanently.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -323,18 +322,15 @@ const deleteAttachment: BaseTool<GetAttachmentArgs> = {
|
||||
}
|
||||
|
||||
const query = `
|
||||
UPDATE attachments
|
||||
SET
|
||||
"deletedAt" = NOW(),
|
||||
"updatedAt" = NOW()
|
||||
WHERE id = $1 AND "deletedAt" IS NULL
|
||||
DELETE FROM attachments
|
||||
WHERE id = $1
|
||||
RETURNING id, key, "documentId"
|
||||
`;
|
||||
|
||||
const result = await pgClient.query(query, [args.id]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
throw new Error('Attachment not found or already deleted');
|
||||
throw new Error('Attachment not found');
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -376,7 +372,7 @@ const getAttachmentStats: BaseTool<{ team_id?: string; document_id?: string }> =
|
||||
},
|
||||
},
|
||||
handler: async (args, pgClient): Promise<ToolResponse> => {
|
||||
const conditions: string[] = ['a."deletedAt" IS NULL'];
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
@@ -396,7 +392,7 @@ const getAttachmentStats: BaseTool<{ team_id?: string; document_id?: string }> =
|
||||
params.push(args.document_id);
|
||||
}
|
||||
|
||||
const whereClause = `WHERE ${conditions.join(' AND ')}`;
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
|
||||
// Overall statistics
|
||||
const overallStatsQuery = await pgClient.query(
|
||||
|
||||
Reference in New Issue
Block a user