This commit is contained in:
Flatlogic Bot 2026-05-14 19:22:48 +00:00
parent 47fbf3afb9
commit 2ec27ee5f1
13 changed files with 432 additions and 87 deletions

View File

@ -9,6 +9,46 @@ const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
function buildVisibleAgentWhere(currentUser) {
if (!currentUser || !currentUser.id) {
return {};
}
return {
[Op.or]: [
{ createdById: currentUser.id },
{ is_default: true },
],
};
}
function buildWritableAgentWhere(currentUser) {
if (!currentUser || !currentUser.id) {
return {};
}
return {
createdById: currentUser.id,
};
}
function mergeWhere(left, right) {
const hasLeft = left && Object.keys(left).length;
const hasRight = right && Object.keys(right).length;
if (!hasLeft) {
return right || {};
}
if (!hasRight) {
return left || {};
}
return {
[Op.and]: [left, right],
};
}
module.exports = class AgentsDBApi {
@ -159,8 +199,17 @@ module.exports = class AgentsDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const agents = await db.agents.findOne({
where: mergeWhere(
{ id },
buildWritableAgentWhere(currentUser),
),
transaction,
});
const agents = await db.agents.findByPk(id, {}, {transaction});
if (!agents) {
throw new Error('Agent not found.');
}
@ -214,14 +263,21 @@ module.exports = class AgentsDBApi {
const transaction = (options && options.transaction) || undefined;
const agents = await db.agents.findAll({
where: {
id: {
[Op.in]: ids,
where: mergeWhere(
{
id: {
[Op.in]: ids,
},
},
},
buildWritableAgentWhere(currentUser),
),
transaction,
});
if (agents.length !== ids.length) {
throw new Error('One or more agents were not found.');
}
await db.sequelize.transaction(async (transaction) => {
for (const record of agents) {
await record.update(
@ -242,7 +298,17 @@ module.exports = class AgentsDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const agents = await db.agents.findByPk(id, options);
const agents = await db.agents.findOne({
where: mergeWhere(
{ id },
buildWritableAgentWhere(currentUser),
),
transaction,
});
if (!agents) {
throw new Error('Agent not found.');
}
await agents.update({
deletedBy: currentUser.id
@ -258,11 +324,20 @@ module.exports = class AgentsDBApi {
}
static async findBy(where, options) {
const currentUser = (options && options.currentUser) || { id: null };
const scopeWhere = options && options.writeAccess
? buildWritableAgentWhere(currentUser)
: buildVisibleAgentWhere(currentUser);
const transaction = (options && options.transaction) || undefined;
const agents = await db.agents.findOne(
{ where },
{ transaction },
{
where: mergeWhere(
where,
scopeWhere,
),
transaction,
},
);
if (!agents) {
@ -297,6 +372,7 @@ module.exports = class AgentsDBApi {
filter,
options
) {
const currentUser = (options && options.currentUser) || { id: null };
const limit = filter.limit || 0;
let offset = 0;
let where = {};
@ -491,7 +567,10 @@ module.exports = class AgentsDBApi {
const queryOptions = {
where,
where: mergeWhere(
where,
buildVisibleAgentWhere(currentUser),
),
include,
distinct: true,
order: filter.field && filter.sort
@ -519,7 +598,8 @@ module.exports = class AgentsDBApi {
}
}
static async findAllAutocomplete(query, limit, offset, ) {
static async findAllAutocomplete(query, limit, offset, options) {
const currentUser = (options && options.currentUser) || { id: null };
let where = {};
@ -539,7 +619,10 @@ module.exports = class AgentsDBApi {
const records = await db.agents.findAll({
attributes: [ 'id', 'name' ],
where,
where: mergeWhere(
where,
buildVisibleAgentWhere(currentUser),
),
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['name', 'ASC']],
@ -553,4 +636,3 @@ module.exports = class AgentsDBApi {
};

View File

@ -9,6 +9,33 @@ const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
function buildOwnedConversationWhere(currentUser) {
if (!currentUser || !currentUser.id) {
return {};
}
return {
userId: currentUser.id,
};
}
function mergeWhere(left, right) {
const hasLeft = left && Object.keys(left).length;
const hasRight = right && Object.keys(right).length;
if (!hasLeft) {
return right || {};
}
if (!hasRight) {
return left || {};
}
return {
[Op.and]: [left, right],
};
}
module.exports = class ConversationsDBApi {
@ -60,7 +87,7 @@ module.exports = class ConversationsDBApi {
);
await conversations.setUser( data.user || null, {
await conversations.setUser( currentUser.id || null, {
transaction,
});
@ -135,8 +162,17 @@ module.exports = class ConversationsDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const conversations = await db.conversations.findOne({
where: mergeWhere(
{ id },
buildOwnedConversationWhere(currentUser),
),
transaction,
});
const conversations = await db.conversations.findByPk(id, {}, {transaction});
if (!conversations) {
throw new Error('Conversation not found.');
}
@ -199,14 +235,21 @@ module.exports = class ConversationsDBApi {
const transaction = (options && options.transaction) || undefined;
const conversations = await db.conversations.findAll({
where: {
id: {
[Op.in]: ids,
where: mergeWhere(
{
id: {
[Op.in]: ids,
},
},
},
buildOwnedConversationWhere(currentUser),
),
transaction,
});
if (conversations.length !== ids.length) {
throw new Error('One or more conversations were not found.');
}
await db.sequelize.transaction(async (transaction) => {
for (const record of conversations) {
await record.update(
@ -227,7 +270,17 @@ module.exports = class ConversationsDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const conversations = await db.conversations.findByPk(id, options);
const conversations = await db.conversations.findOne({
where: mergeWhere(
{ id },
buildOwnedConversationWhere(currentUser),
),
transaction,
});
if (!conversations) {
throw new Error('Conversation not found.');
}
await conversations.update({
deletedBy: currentUser.id
@ -243,11 +296,17 @@ module.exports = class ConversationsDBApi {
}
static async findBy(where, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const conversations = await db.conversations.findOne(
{ where },
{ transaction },
{
where: mergeWhere(
where,
buildOwnedConversationWhere(currentUser),
),
transaction,
},
);
if (!conversations) {
@ -292,6 +351,7 @@ module.exports = class ConversationsDBApi {
filter,
options
) {
const currentUser = (options && options.currentUser) || { id: null };
const limit = filter.limit || 0;
let offset = 0;
let where = {};
@ -478,7 +538,10 @@ module.exports = class ConversationsDBApi {
const queryOptions = {
where,
where: mergeWhere(
where,
buildOwnedConversationWhere(currentUser),
),
include,
distinct: true,
order: filter.field && filter.sort
@ -506,7 +569,8 @@ module.exports = class ConversationsDBApi {
}
}
static async findAllAutocomplete(query, limit, offset, ) {
static async findAllAutocomplete(query, limit, offset, options) {
const currentUser = (options && options.currentUser) || { id: null };
let where = {};
@ -526,7 +590,10 @@ module.exports = class ConversationsDBApi {
const records = await db.conversations.findAll({
attributes: [ 'id', 'title' ],
where,
where: mergeWhere(
where,
buildOwnedConversationWhere(currentUser),
),
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['title', 'ASC']],
@ -540,4 +607,3 @@ module.exports = class ConversationsDBApi {
};

View File

@ -9,6 +9,33 @@ const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
function buildOwnedConversationWhere(currentUser) {
if (!currentUser || !currentUser.id) {
return {};
}
return {
userId: currentUser.id,
};
}
function mergeWhere(left, right) {
const hasLeft = left && Object.keys(left).length;
const hasRight = right && Object.keys(right).length;
if (!hasLeft) {
return right || {};
}
if (!hasRight) {
return left || {};
}
return {
[Op.and]: [left, right],
};
}
module.exports = class MessagesDBApi {
@ -173,8 +200,24 @@ module.exports = class MessagesDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const messages = await db.messages.findOne({
where: {
id,
},
include: [
{
model: db.conversations,
as: 'conversation',
where: buildOwnedConversationWhere(currentUser),
required: true,
},
],
transaction,
});
const messages = await db.messages.findByPk(id, {}, {transaction});
if (!messages) {
throw new Error('Message not found.');
}
@ -254,9 +297,21 @@ module.exports = class MessagesDBApi {
[Op.in]: ids,
},
},
include: [
{
model: db.conversations,
as: 'conversation',
where: buildOwnedConversationWhere(currentUser),
required: true,
},
],
transaction,
});
if (messages.length !== ids.length) {
throw new Error('One or more messages were not found.');
}
await db.sequelize.transaction(async (transaction) => {
for (const record of messages) {
await record.update(
@ -277,7 +332,24 @@ module.exports = class MessagesDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const messages = await db.messages.findByPk(id, options);
const messages = await db.messages.findOne({
where: {
id,
},
include: [
{
model: db.conversations,
as: 'conversation',
where: buildOwnedConversationWhere(currentUser),
required: true,
},
],
transaction,
});
if (!messages) {
throw new Error('Message not found.');
}
await messages.update({
deletedBy: currentUser.id
@ -293,11 +365,22 @@ module.exports = class MessagesDBApi {
}
static async findBy(where, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const messages = await db.messages.findOne(
{ where },
{ transaction },
{
where,
include: [
{
model: db.conversations,
as: 'conversation',
where: buildOwnedConversationWhere(currentUser),
required: true,
},
],
transaction,
},
);
if (!messages) {
@ -342,6 +425,7 @@ module.exports = class MessagesDBApi {
filter,
options
) {
const currentUser = (options && options.currentUser) || { id: null };
const limit = filter.limit || 0;
let offset = 0;
let where = {};
@ -363,16 +447,20 @@ module.exports = class MessagesDBApi {
model: db.conversations,
as: 'conversation',
where: filter.conversation ? {
[Op.or]: [
{ id: { [Op.in]: filter.conversation.split('|').map(term => Utils.uuid(term)) } },
{
title: {
[Op.or]: filter.conversation.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
}
},
]
} : {},
where: mergeWhere(
filter.conversation ? {
[Op.or]: [
{ id: { [Op.in]: filter.conversation.split('|').map(term => Utils.uuid(term)) } },
{
title: {
[Op.or]: filter.conversation.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
}
},
]
} : {},
buildOwnedConversationWhere(currentUser),
),
required: true,
},
@ -626,7 +714,8 @@ module.exports = class MessagesDBApi {
}
}
static async findAllAutocomplete(query, limit, offset, ) {
static async findAllAutocomplete(query, limit, offset, options) {
const currentUser = (options && options.currentUser) || { id: null };
let where = {};
@ -647,6 +736,15 @@ module.exports = class MessagesDBApi {
const records = await db.messages.findAll({
attributes: [ 'id', 'content' ],
where,
include: [
{
model: db.conversations,
as: 'conversation',
where: buildOwnedConversationWhere(currentUser),
required: true,
attributes: [],
},
],
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['content', 'ASC']],
@ -660,4 +758,3 @@ module.exports = class MessagesDBApi {
};

View File

@ -9,6 +9,33 @@ const Utils = require('../utils');
const Sequelize = db.Sequelize;
const Op = Sequelize.Op;
function buildOwnedUsageEventsWhere(currentUser) {
if (!currentUser || !currentUser.id) {
return {};
}
return {
userId: currentUser.id,
};
}
function mergeWhere(left, right) {
const hasLeft = left && Object.keys(left).length;
const hasRight = right && Object.keys(right).length;
if (!hasLeft) {
return right || {};
}
if (!hasRight) {
return left || {};
}
return {
[Op.and]: [left, right],
};
}
module.exports = class Usage_eventsDBApi {
@ -171,8 +198,17 @@ module.exports = class Usage_eventsDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const usage_events = await db.usage_events.findOne({
where: mergeWhere(
{ id },
buildOwnedUsageEventsWhere(currentUser),
),
transaction,
});
const usage_events = await db.usage_events.findByPk(id, {}, {transaction});
if (!usage_events) {
throw new Error('Usage event not found.');
}
@ -262,14 +298,21 @@ module.exports = class Usage_eventsDBApi {
const transaction = (options && options.transaction) || undefined;
const usage_events = await db.usage_events.findAll({
where: {
id: {
[Op.in]: ids,
where: mergeWhere(
{
id: {
[Op.in]: ids,
},
},
},
buildOwnedUsageEventsWhere(currentUser),
),
transaction,
});
if (usage_events.length !== ids.length) {
throw new Error('One or more usage events were not found.');
}
await db.sequelize.transaction(async (transaction) => {
for (const record of usage_events) {
await record.update(
@ -290,7 +333,17 @@ module.exports = class Usage_eventsDBApi {
const currentUser = (options && options.currentUser) || {id: null};
const transaction = (options && options.transaction) || undefined;
const usage_events = await db.usage_events.findByPk(id, options);
const usage_events = await db.usage_events.findOne({
where: mergeWhere(
{ id },
buildOwnedUsageEventsWhere(currentUser),
),
transaction,
});
if (!usage_events) {
throw new Error('Usage event not found.');
}
await usage_events.update({
deletedBy: currentUser.id
@ -306,11 +359,17 @@ module.exports = class Usage_eventsDBApi {
}
static async findBy(where, options) {
const currentUser = (options && options.currentUser) || { id: null };
const transaction = (options && options.transaction) || undefined;
const usage_events = await db.usage_events.findOne(
{ where },
{ transaction },
{
where: mergeWhere(
where,
buildOwnedUsageEventsWhere(currentUser),
),
transaction,
},
);
if (!usage_events) {
@ -357,6 +416,7 @@ module.exports = class Usage_eventsDBApi {
filter,
options
) {
const currentUser = (options && options.currentUser) || { id: null };
const limit = filter.limit || 0;
let offset = 0;
let where = {};
@ -670,7 +730,10 @@ module.exports = class Usage_eventsDBApi {
const queryOptions = {
where,
where: mergeWhere(
where,
buildOwnedUsageEventsWhere(currentUser),
),
include,
distinct: true,
order: filter.field && filter.sort
@ -698,7 +761,8 @@ module.exports = class Usage_eventsDBApi {
}
}
static async findAllAutocomplete(query, limit, offset, ) {
static async findAllAutocomplete(query, limit, offset, options) {
const currentUser = (options && options.currentUser) || { id: null };
let where = {};
@ -718,7 +782,10 @@ module.exports = class Usage_eventsDBApi {
const records = await db.usage_events.findAll({
attributes: [ 'id', 'provider' ],
where,
where: mergeWhere(
where,
buildOwnedUsageEventsWhere(currentUser),
),
limit: limit ? Number(limit) : undefined,
offset: offset ? Number(offset) : undefined,
orderBy: [['provider', 'ASC']],
@ -732,4 +799,3 @@ module.exports = class Usage_eventsDBApi {
};

View File

@ -356,7 +356,6 @@ router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await AgentsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
@ -394,7 +393,7 @@ router.get('/autocomplete', async (req, res) => {
req.query.query,
req.query.limit,
req.query.offset,
{ currentUser: req.currentUser }
);
res.status(200).send(payload);
@ -435,9 +434,13 @@ router.get('/autocomplete', async (req, res) => {
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await AgentsDBApi.findBy(
{ id: req.params.id },
{ currentUser: req.currentUser },
);
if (!payload) {
res.status(404).send({ message: 'Agent not found.' });
return;
}
res.status(200).send(payload);
}));

View File

@ -345,7 +345,6 @@ router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await ConversationsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
@ -383,7 +382,7 @@ router.get('/autocomplete', async (req, res) => {
req.query.query,
req.query.limit,
req.query.offset,
{ currentUser: req.currentUser }
);
res.status(200).send(payload);
@ -424,9 +423,13 @@ router.get('/autocomplete', async (req, res) => {
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await ConversationsDBApi.findBy(
{ id: req.params.id },
{ currentUser: req.currentUser },
);
if (!payload) {
res.status(404).send({ message: 'Conversation not found.' });
return;
}
res.status(200).send(payload);
}));

View File

@ -355,7 +355,6 @@ router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await MessagesDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
@ -393,7 +392,7 @@ router.get('/autocomplete', async (req, res) => {
req.query.query,
req.query.limit,
req.query.offset,
{ currentUser: req.currentUser }
);
res.status(200).send(payload);
@ -434,9 +433,13 @@ router.get('/autocomplete', async (req, res) => {
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await MessagesDBApi.findBy(
{ id: req.params.id },
{ currentUser: req.currentUser },
);
if (!payload) {
res.status(404).send({ message: 'Message not found.' });
return;
}
res.status(200).send(payload);
}));

View File

@ -357,7 +357,6 @@ router.get('/count', wrapAsync(async (req, res) => {
const currentUser = req.currentUser;
const payload = await Usage_eventsDBApi.findAll(
req.query,
null,
{ countOnly: true, currentUser }
);
@ -395,7 +394,7 @@ router.get('/autocomplete', async (req, res) => {
req.query.query,
req.query.limit,
req.query.offset,
{ currentUser: req.currentUser }
);
res.status(200).send(payload);
@ -436,9 +435,13 @@ router.get('/autocomplete', async (req, res) => {
router.get('/:id', wrapAsync(async (req, res) => {
const payload = await Usage_eventsDBApi.findBy(
{ id: req.params.id },
{ currentUser: req.currentUser },
);
if (!payload) {
res.status(404).send({ message: 'Usage event not found.' });
return;
}
res.status(200).send(payload);
}));

View File

@ -70,7 +70,11 @@ module.exports = class AgentsService {
try {
let agents = await AgentsDBApi.findBy(
{id},
{transaction},
{
currentUser,
transaction,
writeAccess: true,
},
);
if (!agents) {
@ -135,4 +139,3 @@ module.exports = class AgentsService {
};

View File

@ -70,7 +70,10 @@ module.exports = class ConversationsService {
try {
let conversations = await ConversationsDBApi.findBy(
{id},
{transaction},
{
currentUser,
transaction,
},
);
if (!conversations) {
@ -135,4 +138,3 @@ module.exports = class ConversationsService {
};

View File

@ -70,7 +70,10 @@ module.exports = class MessagesService {
try {
let messages = await MessagesDBApi.findBy(
{id},
{transaction},
{
currentUser,
transaction,
},
);
if (!messages) {
@ -135,4 +138,3 @@ module.exports = class MessagesService {
};

View File

@ -70,7 +70,10 @@ module.exports = class Usage_eventsService {
try {
let usage_events = await Usage_eventsDBApi.findBy(
{id},
{transaction},
{
currentUser,
transaction,
},
);
if (!usage_events) {
@ -135,4 +138,3 @@ module.exports = class Usage_eventsService {
};

View File

@ -26,6 +26,20 @@ function cleanMarkdownPreview(value) {
.trim();
}
function buildVisibleAgentWhere(currentUser) {
return {
is_active: true,
[Op.or]: [
{
createdById: currentUser.id,
},
{
is_default: true,
},
],
};
}
function buildConversationTitle(content) {
const normalized = cleanMarkdownPreview(content);
@ -205,11 +219,11 @@ async function findLatestUserMessageBeforeSequence(conversationId, sequence, tra
async function resolveConversationAgent(conversation, currentUser, transaction) {
let agent = conversation.agent;
if (agent) {
if (agent && (agent.createdById === currentUser.id || agent.is_default)) {
return agent;
}
agent = await findDefaultAgent(transaction);
agent = await findDefaultAgent(currentUser, transaction);
if (!agent) {
return null;
@ -463,7 +477,7 @@ function sortConversations(conversations) {
});
}
async function findDefaultAgent(transaction) {
async function findDefaultAgent(currentUser, transaction) {
const defaultAgent = await db.agents.findOne({
where: {
is_active: true,
@ -480,6 +494,7 @@ async function findDefaultAgent(transaction) {
return db.agents.findOne({
where: {
is_active: true,
createdById: currentUser.id,
},
order: [['createdAt', 'ASC']],
transaction,
@ -529,9 +544,7 @@ module.exports = class WorkspaceService {
static async bootstrap(currentUser) {
const [agents, conversations] = await Promise.all([
db.agents.findAll({
where: {
is_active: true,
},
where: buildVisibleAgentWhere(currentUser),
order: [
['is_default', 'DESC'],
['name', 'ASC'],
@ -614,14 +627,14 @@ module.exports = class WorkspaceService {
agent = await db.agents.findOne({
where: {
id: data.agentId,
is_active: true,
...buildVisibleAgentWhere(currentUser),
},
transaction,
});
}
if (!agent) {
agent = await findDefaultAgent(transaction);
agent = await findDefaultAgent(currentUser, transaction);
}
const conversation = await db.conversations.create(
@ -686,7 +699,7 @@ module.exports = class WorkspaceService {
const agent = await db.agents.findOne({
where: {
id: data.agentId,
is_active: true,
...buildVisibleAgentWhere(currentUser),
},
transaction,
});
@ -809,7 +822,7 @@ module.exports = class WorkspaceService {
agent = await db.agents.findOne({
where: {
id: data.agentId,
is_active: true,
...buildVisibleAgentWhere(currentUser),
},
transaction: createTransaction,
});