• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

statuscompliance / status-backend / 15819810163

23 Jun 2025 08:56AM UTC coverage: 84.833% (-0.03%) from 84.86%
15819810163

push

github

alvarobernal2412
refactor(endpoint): simplify endpointAvailable logic for better readability

870 of 1053 branches covered (82.62%)

Branch coverage included in aggregate %.

3 of 7 new or added lines in 3 files covered. (42.86%)

39 existing lines in 1 file now uncovered.

1742 of 2026 relevant lines covered (85.98%)

19.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

3.97
/src/controllers/thread.controller.js
1
import { models } from '../models/models.js';
2
import { getSequelize } from '../db/database.js';
3
import OpenAI from 'openai';
4
import jwt from 'jsonwebtoken';
5

6
const openai = new OpenAI({
52✔
7
  organization: process.env.AI_ORG_ID || 'org-id',
104✔
8
  apiKey: process.env.AI_API_KEY || 'api-key',
104✔
9
});
10

11
export async function getThreads(req, res) {
12
  try {
×
13
    const threads = await models.Thread.findAll();
×
14
    res.status(200).json(threads);
×
15
  } catch (error) {
16
    res.status(500).json({ message: `Failed to get all threads, error: ${error.message}` });
×
17
  }   
18
}
19

20
export async function getThreadsByUserId(req, res) {
21
  try {
×
22
    const userId = getUserId(req)
×
23
    const threads = await models.Thread.findAll({
×
24
      where: {
25
        UserId: userId
26
      }
27
    });
28
    res.status(200).json(threads);
×
29
  } catch (error) {
30
    res.status(500).json({ message: `Failed to get user threads, error: ${error.message}` })
×
31
  }
32
}
33

34
export async function deleteThread(req, res) {
35
  try {
×
36
    const { gptId } = req.params
×
37

38
    const thread = await models.Thread.findAll({
×
39
      where: {
40
        gpt_id: gptId
41
      }
42
    });
43

44
    if (thread[0].length === 0) {
×
45
      return res.status(404).json({ message: `Thread ${gptId} not found` })
×
46
    }
47
    const threadId = thread[0][0].id
×
48

49
    await models.Message.destroy({
×
50
      where: {
51
        threadId: threadId
52
      }
53
    });
54
    await models.Thread.destroy({
×
55
      where: {
56
        gpt_id: gptId
57
      }
58
    });
59

60
    await openai.beta.threads.del(gptId)
×
61
    res.status(200).json({ message: `Thread ${gptId} deleted successfully` })
×
62
  } catch (error) {
63
    console.error(error);
×
64
    res.status(500).json({ message: `Failed to delete thread, error: ${error.message}` })
×
65
  }
66
}
67

68
const validMsg = (content) => {
52✔
69
  let charactersLength;
70
  let words;
71
  if (typeof content !== 'string') {
×
72
    charactersLength = content.length;
×
73
    words = JSON.parse(content).split(' ');
×
74
  } else {
75
    words = content.split(' ');
×
76
    charactersLength = content.length;
×
77
  }
78
  return words.length >= 6 || charactersLength >= 40;
×
79
}
80

81
export async function createThread(req, res) {
82
  const { assistantId } = req.body;
×
83
  const { content } = req.body;
×
84

85
  const accessToken = req.headers['authorization'].split(' ')[1] || req.headers['Authorization'].split(' ')[1];
×
86
  if(isValidAccessToken(req.headers['authorization'])){
×
87
    const decoded = jwt.verify(accessToken, process.env.JWT_SECRET);
×
88
    const username = decoded.username;
×
89
    try {
×
90
      const user = await models.User.findOne({ where: { username: username } });
×
91
      const userId = user.id;
×
92

93
      if (!validMsg(content)) {
×
94
        return res.status(400).json({ error: 'Por favor, haz el mensaje un poco más detallado' });
×
95
      }
NEW
96
      const sequelize = getSequelize();
×
97
      await sequelize.transaction(async (transaction) => {
×
98
        const runThread = await openai.beta.threads.createAndRun({
×
99
          assistant_id: assistantId,
100
          thread: {
101
            messages: [{ role: 'user', content: content }],
102
          },
103
        });
UNCOV
104
        const threadId = await insertThread(transaction, runThread.thread_id, userId, runThread.id);
×
105

UNCOV
106
        await insertMessage(transaction, content, threadId);
×
107

UNCOV
108
        return res.status(201).json({ id: runThread.thread_id, message: 'Hilo creado exitosamente' });
×
109
      });
110
    } catch (error) {
UNCOV
111
      console.error(error);
×
112
      return res.status(500).json({ error: `Error al crear el hilo, error: ${error}` });
×
113
    }
114
  } else {
UNCOV
115
    return res.status(401).json({ error: 'Unauthorized' });
×
116
  }
117
}
118

119
export async function addNewMessage(req, res) {
UNCOV
120
  const { gptId } = req.params;
×
121
  const { content, assistantId } = req.body;
×
122

UNCOV
123
  try {
×
124
    if (!validMsg(content)) {
×
125
      return res.status(400).json({ error: 'El mensaje debe tener al menos 15 palabras' });
×
126
    }
127

NEW
128
    const sequelize = getSequelize();
×
129
    await sequelize.transaction(async (transaction) => {
×
130
      await openai.beta.threads.messages.create(gptId, { role: 'user', content: content });
×
131

132
      await openai.beta.threads.runs.create(gptId, { assistant_id: assistantId });
×
133

134
      const threadId = await models.Thread.findOne({ where: { gpt_id: gptId } }, { transaction });
×
135

136
      await insertMessage(transaction, content, threadId.id);
×
137

138
      return res.status(201).json({ message: 'Mensaje agregado exitosamente' });
×
139
    });
140
  } catch (error) {
UNCOV
141
    console.error(error);
×
UNCOV
142
    return res.status(500).json({ error: 'Error al crear el hilo' });
×
143
  }
144
}
145

146

147
export async function getThreadMessages(req, res) {
UNCOV
148
  const { gptId } = req.params
×
UNCOV
149
  try {
×
150
    const thread = await models.Thread.findOne({
×
151
      where: {
152
        gpt_id: gptId
153
      }
154
    });
UNCOV
155
    const runId = thread.run_id;
×
UNCOV
156
    const runStatus = await openai.beta.threads.runs.retrieve(gptId, runId);
×
157
    if (runStatus.status === 'completed') {
×
158
      const messageList = await openai.beta.threads.messages.list(gptId);
×
159
      res.status(200).json(messageList);
×
160
    } else {
161
      res.status(200).json({ message: 'Run not completed yet' });
×
162
    }
163
  } catch (error) {
UNCOV
164
    console.error(error);
×
UNCOV
165
    res.status(500).json({ error: `Error al obtener mensajes del hilo. Error: ${error}` });
×
166
  }
167
}
168

169
export async function deleteUserThreads(req,res){
UNCOV
170
  const accessToken = req.headers['authorization'].split(' ')[1] || req.headers['Authorization'].split(' ')[1]
×
UNCOV
171
  const decoded = jwt.verify(accessToken, process.env.JWT_SECRET)
×
172
  const userId = decoded.user_id
×
173
  try {
×
NEW
174
    const sequelize = getSequelize();
×
175
    await sequelize.transaction(async (transaction) => {
×
176
      await models.Message.destroy({
×
177
        where: {
178
          threadId: userId
179
        }
180
      }, { transaction });
UNCOV
181
      await models.Thread.destroy({
×
182
        where: {
183
          UserId: userId
184
        }
185
      }, { transaction });
UNCOV
186
      res.status(200).json({ message: 'Threads deleted successfully' });
×
187
    });
188
  } catch (error) {
UNCOV
189
    console.error(error);
×
UNCOV
190
    res.status(500).json({ message: `Failed to delete threads, error: ${error.message}` });
×
191
  }
192
}
193

194
export async function changeThreadName(req, res){
UNCOV
195
  const userId = getUserId(req)
×
UNCOV
196
  const { gptId } = req.params
×
197
  const { name } = req.body
×
198
  try {
×
199

200
    const thread = await models.Thread.update({
×
201
      name
202
    }, {
203
      where: {
204
        gpt_id: gptId,
205
        UserId: userId
206
      }
207
    });
208

UNCOV
209
    if (thread[0].length === 0) {
×
UNCOV
210
      return res.status(404).json({ message: `Thread ${gptId} not found` })
×
211
    }
212
    res.status(200).json({ message: `Thread ${gptId} name updated successfully` })
×
213
  } catch (error) {
214
    console.error(error);
×
UNCOV
215
    res.status(500).json({ message: `Failed to update thread name, error: ${error.message}` })
×
216
  }
217
}
218

219
async function insertThread(transaction, gptId, userId, runId){
UNCOV
220
  try {
×
UNCOV
221
    const result = await models.Thread.create({
×
222
      gpt_id: gptId,
223
      UserId: userId,
224
      name: 'Nuevo Hilo',
225
      run_id: runId
226
    }, { transaction });
UNCOV
227
    return result.id;
×
228
  } catch (error) {
229
    console.error(error);
×
UNCOV
230
    throw new Error(`Error al insertar el hilo en la base de datos, error: ${error.message}`);
×
231
  }
232
}
233

234
async function insertMessage(transaction, content, threadId){
UNCOV
235
  try {
×
UNCOV
236
    const result = await models.Message.create({
×
237
      content,
238
      threadId: threadId
239
    }, { transaction });
UNCOV
240
    return result.id;
×
241
  } catch (error) {
242
    console.error(error);
×
UNCOV
243
    throw new Error(`Error al insertar el mensaje en la base de datos, error: ${error.message}`);
×
244
  }
245
}
246

247
function isValidAccessToken(token) {
UNCOV
248
  if (typeof token !== 'string' || token.length === 0) {
×
UNCOV
249
    return false;
×
250
  }
251
  const [bearer, accessToken] = token.split(' ');
×
UNCOV
252
  return bearer.toLowerCase() === 'bearer' && accessToken.length > 0;
×
253
}
254

255
function getUserId(req){
UNCOV
256
  if(req.headers['authorization'].split(' ')[1] || req.headers['Authorization'].split(' ')[1]){
×
UNCOV
257
    const accessToken = req.headers['authorization'].split(' ')[1] || req.headers['Authorization'].split(' ')[1]
×
258
    const decoded = jwt.verify(accessToken, process.env.JWT_SECRET)
×
259
    const userId = decoded.user_id
×
260
    return userId
×
261
  } else {
262
    throw new Error('No token provided')
×
263
  }
264
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc