WIP - add and remove shares

This commit is contained in:
Francisco Gaona
2025-12-30 21:42:42 +01:00
parent e73126bcb7
commit c50098a55c
2 changed files with 44 additions and 16 deletions

View File

@@ -9,7 +9,7 @@ export interface RecordShareAccessLevel {
export class RecordShare extends BaseModel { export class RecordShare extends BaseModel {
static tableName = 'record_shares'; static tableName = 'record_shares';
// Disable automatic snake_case conversion for this table // Don't use snake_case mapping since DB columns are already camelCase
static get columnNameMappers() { static get columnNameMappers() {
return { return {
parse(obj: any) { parse(obj: any) {
@@ -21,6 +21,15 @@ export class RecordShare extends BaseModel {
}; };
} }
// Override BaseModel hooks to prevent automatic timestamp handling
$beforeInsert(queryContext: any) {
// Don't set timestamps - let database defaults handle it
}
$beforeUpdate(opt: any, queryContext: any) {
// Don't set timestamps - let database defaults handle it
}
id!: string; id!: string;
objectDefinitionId!: string; objectDefinitionId!: string;
recordId!: string; recordId!: string;
@@ -30,6 +39,7 @@ export class RecordShare extends BaseModel {
expiresAt?: Date; expiresAt?: Date;
revokedAt?: Date; revokedAt?: Date;
createdAt!: Date; createdAt!: Date;
updatedAt!: Date;
static get jsonSchema() { static get jsonSchema() {
return { return {
@@ -49,8 +59,22 @@ export class RecordShare extends BaseModel {
canDelete: { type: 'boolean' }, canDelete: { type: 'boolean' },
}, },
}, },
expiresAt: { type: ['string', 'null'], format: 'date-time' }, expiresAt: {
revokedAt: { type: ['string', 'null'], format: 'date-time' }, anyOf: [
{ type: 'string', format: 'date-time' },
{ type: 'null' },
{ type: 'object' } // Allow Date objects
]
},
revokedAt: {
anyOf: [
{ type: 'string', format: 'date-time' },
{ type: 'null' },
{ type: 'object' } // Allow Date objects
]
},
createdAt: { type: ['string', 'object'], format: 'date-time' },
updatedAt: { type: ['string', 'object'], format: 'date-time' },
}, },
}; };
} }

View File

@@ -147,14 +147,16 @@ export class RecordSharingController {
if (existingShare) { if (existingShare) {
// Update existing share // Update existing share
await RecordShare.query(knex) await knex('record_shares')
.patchAndFetchById(existingShare.id, { .where({ id: existingShare.id })
accessLevel: { .update({
accessLevel: JSON.stringify({
canRead: data.canRead, canRead: data.canRead,
canEdit: data.canEdit, canEdit: data.canEdit,
canDelete: data.canDelete, canDelete: data.canDelete,
}, }),
expiresAt: data.expiresAt ? new Date(data.expiresAt) : null, expiresAt: data.expiresAt ? data.expiresAt : null,
updatedAt: knex.fn.now(),
}); });
return RecordShare.query(knex) return RecordShare.query(knex)
@@ -163,21 +165,21 @@ export class RecordSharingController {
} }
// Create new share // Create new share
const share = await RecordShare.query(knex).insert({ const [shareId] = await knex('record_shares').insert({
objectDefinitionId: objectDef.id, objectDefinitionId: objectDef.id,
recordId, recordId,
granteeUserId: data.granteeUserId, granteeUserId: data.granteeUserId,
grantedByUserId: currentUser.userId, grantedByUserId: currentUser.userId,
accessLevel: { accessLevel: JSON.stringify({
canRead: data.canRead, canRead: data.canRead,
canEdit: data.canEdit, canEdit: data.canEdit,
canDelete: data.canDelete, canDelete: data.canDelete,
}, }),
expiresAt: data.expiresAt ? new Date(data.expiresAt) : null, expiresAt: data.expiresAt ? data.expiresAt : null,
}); });
return RecordShare.query(knex) return RecordShare.query(knex)
.findById(share.id) .findById(shareId)
.withGraphFetched('[granteeUser]'); .withGraphFetched('[granteeUser]');
} }
@@ -233,9 +235,11 @@ export class RecordSharingController {
} }
// Revoke the share (soft delete) // Revoke the share (soft delete)
await RecordShare.query(knex) await knex('record_shares')
.patchAndFetchById(shareId, { .where({ id: shareId })
revokedAt: new Date(), .update({
revokedAt: knex.fn.now(),
updatedAt: knex.fn.now(),
}); });
return { success: true }; return { success: true };