Rewritten minLogLevel feature

1f9fa747865e3ad9f4160c16f4e2eb3443cd1baf

Eradev <admin@eradev.com>

44 files changed, +578 -559Showing whitespace changes
default/config.yaml+2 -0
@@ -65,6 +65,8 @@ autheliaAuth: false
65# the username and passwords for basic auth are the same as those65# the username and passwords for basic auth are the same as those
66# for the individual accounts66# for the individual accounts
67perUserBasicAuth: false67perUserBasicAuth: false
68# Minimum log level to display in the terminal (DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3)
69minLogLevel: 0
6870
69# User session timeout *in seconds* (defaults to 24 hours).71# User session timeout *in seconds* (defaults to 24 hours).
70## Set to a positive number to expire session after a certain time of inactivity72## Set to a positive number to expire session after a certain time of inactivity
public/scripts/chat-templates.js+2 -1
@@ -59,6 +59,7 @@ const hash_derivations = {
59 // Tulu-3-8B59 // Tulu-3-8B
60 // Tulu-3-70B60 // Tulu-3-70B
61 'Tulu'61 'Tulu'
62 ,
62};63};
6364
64const substr_derivations = {65const substr_derivations = {
@@ -87,6 +88,6 @@ export async function deriveTemplatesFromChatTemplate(chat_template, hash) {
87 }88 }
88 }89 }
8990
90 console.log(`Unknown chat template hash: ${hash} for [${chat_template}]`);91 console.warn(`Unknown chat template hash: ${hash} for [${chat_template}]`);
91 return null;92 return null;
92}93}
public/scripts/extensions/quick-reply/src/QuickReplySet.js+5 -28
@@ -1,15 +1,13 @@
1import { getRequestHeaders, substituteParams } from '../../../../script.js';1import { getRequestHeaders, substituteParams } from '../../../../script.js';
2import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';2import { Popup, POPUP_RESULT, POPUP_TYPE } from '../../../popup.js';
3import { executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';3import { executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';
4import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';4import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
5import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';5import { debounceAsync, warn } from '../index.js';
6import { debounceAsync, log, warn } from '../index.js';
7import { QuickReply } from './QuickReply.js';6import { QuickReply } from './QuickReply.js';
87
9export class QuickReplySet {8export class QuickReplySet {
10 /**@type {QuickReplySet[]}*/ static list = [];9 /**@type {QuickReplySet[]}*/ static list = [];
1110
12
13 static from(props) {11 static from(props) {
14 props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));12 props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));
15 const instance = Object.assign(new this(), props);13 const instance = Object.assign(new this(), props);
@@ -24,9 +22,6 @@ export class QuickReplySet {
24 return this.list.find(it=>it.name == name);22 return this.list.find(it=>it.name == name);
25 }23 }
2624
27
28
29
30 /**@type {string}*/ name;25 /**@type {string}*/ name;
31 /**@type {boolean}*/ disableSend = false;26 /**@type {boolean}*/ disableSend = false;
32 /**@type {boolean}*/ placeBeforeInput = false;27 /**@type {boolean}*/ placeBeforeInput = false;
@@ -34,19 +29,12 @@ export class QuickReplySet {
34 /**@type {string}*/ color = 'transparent';29 /**@type {string}*/ color = 'transparent';
35 /**@type {boolean}*/ onlyBorderColor = false;30 /**@type {boolean}*/ onlyBorderColor = false;
36 /**@type {QuickReply[]}*/ qrList = [];31 /**@type {QuickReply[]}*/ qrList = [];
37
38 /**@type {number}*/ idIndex = 0;32 /**@type {number}*/ idIndex = 0;
39
40 /**@type {boolean}*/ isDeleted = false;33 /**@type {boolean}*/ isDeleted = false;
41
42 /**@type {function}*/ save;34 /**@type {function}*/ save;
43
44 /**@type {HTMLElement}*/ dom;35 /**@type {HTMLElement}*/ dom;
45 /**@type {HTMLElement}*/ settingsDom;36 /**@type {HTMLElement}*/ settingsDom;
4637
47
48
49
50 constructor() {38 constructor() {
51 this.save = debounceAsync(()=>this.performSave(), 200);39 this.save = debounceAsync(()=>this.performSave(), 200);
52 }40 }
@@ -55,9 +43,6 @@ export class QuickReplySet {
55 this.qrList.forEach(qr=>this.hookQuickReply(qr));43 this.qrList.forEach(qr=>this.hookQuickReply(qr));
56 }44 }
5745
58
59
60
61 unrender() {46 unrender() {
62 this.dom?.remove();47 this.dom?.remove();
63 this.dom = null;48 this.dom = null;
@@ -100,9 +85,6 @@ export class QuickReplySet {
100 }85 }
101 }86 }
10287
103
104
105
106 renderSettings() {88 renderSettings() {
107 if (!this.settingsDom) {89 if (!this.settingsDom) {
108 this.settingsDom = document.createElement('div'); {90 this.settingsDom = document.createElement('div'); {
@@ -123,9 +105,6 @@ export class QuickReplySet {
123 this.settingsDom.append(qr.renderSettings(idx));105 this.settingsDom.append(qr.renderSettings(idx));
124 }106 }
125107
126
127
128
129 /**108 /**
130 *109 *
131 * @param {QuickReply} qr110 * @param {QuickReply} qr
@@ -138,6 +117,7 @@ export class QuickReplySet {
138 closure.scope.setMacro('arg::*', '');117 closure.scope.setMacro('arg::*', '');
139 return (await closure.execute())?.pipe;118 return (await closure.execute())?.pipe;
140 }119 }
120
141 /**121 /**
142 *122 *
143 * @param {QuickReply} qr The QR to execute.123 * @param {QuickReply} qr The QR to execute.
@@ -207,6 +187,7 @@ export class QuickReplySet {
207 document.querySelector('#send_but').click();187 document.querySelector('#send_but').click();
208 }188 }
209 }189 }
190
210 /**191 /**
211 * @param {QuickReply} qr192 * @param {QuickReply} qr
212 * @param {string} [message] - optional altered message to be used193 * @param {string} [message] - optional altered message to be used
@@ -220,9 +201,6 @@ export class QuickReplySet {
220 });201 });
221 }202 }
222203
223
224
225
226 addQuickReply(data = {}) {204 addQuickReply(data = {}) {
227 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;205 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;
228 data.id =206 data.id =
@@ -239,6 +217,7 @@ export class QuickReplySet {
239 this.save();217 this.save();
240 return qr;218 return qr;
241 }219 }
220
242 addQuickReplyFromText(qrJson) {221 addQuickReplyFromText(qrJson) {
243 let data;222 let data;
244 if (qrJson) {223 if (qrJson) {
@@ -371,7 +350,6 @@ export class QuickReplySet {
371 this.save();350 this.save();
372 }351 }
373352
374
375 toJSON() {353 toJSON() {
376 return {354 return {
377 version: 2,355 version: 2,
@@ -386,7 +364,6 @@ export class QuickReplySet {
386 };364 };
387 }365 }
388366
389
390 async performSave() {367 async performSave() {
391 const response = await fetch('/api/quick-replies/save', {368 const response = await fetch('/api/quick-replies/save', {
392 method: 'POST',369 method: 'POST',
public/scripts/extensions/vectors/index.js+2 -4
@@ -1638,14 +1638,12 @@ jQuery(async () => {
1638 }1638 }
1639 return textResult;1639 return textResult;
1640 };1640 };
1641
1642 if (args.return === 'chunks') {1641 if (args.return === 'chunks') {
1643 return getChunksText();1642 return getChunksText();
1644 }1643 }
16451644
1646 // @ts-ignore1645 // @ts-ignore
1647 return slashCommandReturnHelper.doReturn(args.return ?? 'object', urls, { objectToStringFunc: list => list.join('\n') });1646 return slashCommandReturnHelper.doReturn(args.return ?? 'object', urls, { objectToStringFunc: list => list.join('\n') });
1648
1649 },1647 },
1650 aliases: ['databank-search', 'data-bank-search'],1648 aliases: ['databank-search', 'data-bank-search'],
1651 helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',1649 helpString: 'Search the Data Bank for a specific query using vector similarity. Returns a list of file URLs with the most relevant content.',
@@ -1660,10 +1658,10 @@ jQuery(async () => {
1660 defaultValue: 'object',1658 defaultValue: 'object',
1661 enumList: [1659 enumList: [
1662 new SlashCommandEnumValue('chunks', 'Return the actual content chunks', enumTypes.enum, '{}'),1660 new SlashCommandEnumValue('chunks', 'Return the actual content chunks', enumTypes.enum, '{}'),
1663 ...slashCommandReturnHelper.enumList({ allowObject: true })1661 ...slashCommandReturnHelper.enumList({ allowObject: true }),
1664 ],1662 ],
1665 forceEnum: true,1663 forceEnum: true,
1666 })1664 }),
1667 ],1665 ],
1668 unnamedArgumentList: [1666 unnamedArgumentList: [
1669 new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),1667 new SlashCommandArgument('Query to search by.', ARGUMENT_TYPE.STRING, true, false),
server.js+6 -2
@@ -67,6 +67,7 @@ import {
67 forwardFetchResponse,67 forwardFetchResponse,
68 removeColorFormatting,68 removeColorFormatting,
69 getSeparator,69 getSeparator,
70 setupLogLevel,
70} from './src/util.js';71} from './src/util.js';
71import { UPLOADS_DIRECTORY } from './src/constants.js';72import { UPLOADS_DIRECTORY } from './src/constants.js';
72import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';73import { ensureThumbnailCache } from './src/endpoints/thumbnails.js';
@@ -688,8 +689,9 @@ const preSetupTasks = async function () {
688function getAutorunHostname() {689function getAutorunHostname() {
689 if (autorunHostname === 'auto') {690 if (autorunHostname === 'auto') {
690 if (enableIPv6 && enableIPv4) {691 if (enableIPv6 && enableIPv4) {
691 if (avoidLocalhost) return '[::1]';692 return avoidLocalhost
692 return 'localhost';693 ? '[::1]'
694 : 'localhost';
693 }695 }
694696
695 if (enableIPv6) {697 if (enableIPv6) {
@@ -755,6 +757,8 @@ const postSetupTasks = async function (v6Failed, v4Failed) {
755 }757 }
756 }758 }
757 }759 }
760
761 setupLogLevel();
758};762};
759763
760/**764/**
src/constants.js+7 -0
@@ -413,3 +413,10 @@ export const VLLM_KEYS = [
413 'guided_decoding_backend',413 'guided_decoding_backend',
414 'guided_whitespace_pattern',414 'guided_whitespace_pattern',
415];415];
416
417export const LOG_LEVELS = {
418 DEBUG: 0,
419 INFO: 1,
420 WARN: 2,
421 ERROR: 3,
422};
src/endpoints/anthropic.js+3 -3
@@ -32,7 +32,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
32 max_tokens: 4096,32 max_tokens: 4096,
33 };33 };
3434
35 console.log('Multimodal captioning request', body);35 console.debug('Multimodal captioning request', body);
3636
37 const result = await fetch(url, {37 const result = await fetch(url, {
38 body: JSON.stringify(body),38 body: JSON.stringify(body),
@@ -46,14 +46,14 @@ router.post('/caption-image', jsonParser, async (request, response) => {
4646
47 if (!result.ok) {47 if (!result.ok) {
48 const text = await result.text();48 const text = await result.text();
49 console.log(`Claude API returned error: ${result.status} ${result.statusText}`, text);49 console.error(`Claude API returned error: ${result.status} ${result.statusText}`, text);
50 return response.status(result.status).send({ error: true });50 return response.status(result.status).send({ error: true });
51 }51 }
5252
53 /** @type {any} */53 /** @type {any} */
54 const generateResponseJson = await result.json();54 const generateResponseJson = await result.json();
55 const caption = generateResponseJson.content[0].text;55 const caption = generateResponseJson.content[0].text;
56 console.log('Claude response:', generateResponseJson);56 console.debug('Claude response:', generateResponseJson);
5757
58 if (!caption) {58 if (!caption) {
59 return response.status(500).send('No caption found');59 return response.status(500).send('No caption found');
src/endpoints/assets.js+29 -18
@@ -176,7 +176,7 @@ router.post('/get', jsonParser, async (request, response) => {
176 }176 }
177 }177 }
178 catch (err) {178 catch (err) {
179 console.log(err);179 console.error(err);
180 }180 }
181 return response.send(output);181 return response.send(output);
182});182});
@@ -200,7 +200,7 @@ router.post('/download', jsonParser, async (request, response) => {
200 category = i;200 category = i;
201201
202 if (category === null) {202 if (category === null) {
203 console.debug('Bad request: unsupported asset category.');203 console.error('Bad request: unsupported asset category.');
204 return response.sendStatus(400);204 return response.sendStatus(400);
205 }205 }
206206
@@ -212,7 +212,7 @@ router.post('/download', jsonParser, async (request, response) => {
212212
213 const temp_path = path.join(request.user.directories.assets, 'temp', request.body.filename);213 const temp_path = path.join(request.user.directories.assets, 'temp', request.body.filename);
214 const file_path = path.join(request.user.directories.assets, category, request.body.filename);214 const file_path = path.join(request.user.directories.assets, category, request.body.filename);
215 console.debug('Request received to download', url, 'to', file_path);215 console.info('Request received to download', url, 'to', file_path);
216216
217 try {217 try {
218 // Download to temp218 // Download to temp
@@ -224,7 +224,9 @@ router.post('/download', jsonParser, async (request, response) => {
224 // Delete if previous download failed224 // Delete if previous download failed
225 if (fs.existsSync(temp_path)) {225 if (fs.existsSync(temp_path)) {
226 fs.unlink(temp_path, (err) => {226 fs.unlink(temp_path, (err) => {
227 if (err) throw err;227 if (err) {
228 throw err;
229 }
228 });230 });
229 }231 }
230 const fileStream = fs.createWriteStream(destination, { flags: 'wx' });232 const fileStream = fs.createWriteStream(destination, { flags: 'wx' });
@@ -241,13 +243,13 @@ router.post('/download', jsonParser, async (request, response) => {
241 }243 }
242244
243 // Move into asset place245 // Move into asset place
244 console.debug('Download finished, moving file from', temp_path, 'to', file_path);246 console.info('Download finished, moving file from', temp_path, 'to', file_path);
245 fs.copyFileSync(temp_path, file_path);247 fs.copyFileSync(temp_path, file_path);
246 fs.rmSync(temp_path);248 fs.rmSync(temp_path);
247 response.sendStatus(200);249 response.sendStatus(200);
248 }250 }
249 catch (error) {251 catch (error) {
250 console.log(error);252 console.error(error);
251 response.sendStatus(500);253 response.sendStatus(500);
252 }254 }
253});255});
@@ -270,7 +272,7 @@ router.post('/delete', jsonParser, async (request, response) => {
270 category = i;272 category = i;
271273
272 if (category === null) {274 if (category === null) {
273 console.debug('Bad request: unsupported asset category.');275 console.error('Bad request: unsupported asset category.');
274 return response.sendStatus(400);276 return response.sendStatus(400);
275 }277 }
276278
@@ -280,25 +282,27 @@ router.post('/delete', jsonParser, async (request, response) => {
280 return response.status(400).send(validation.message);282 return response.status(400).send(validation.message);
281283
282 const file_path = path.join(request.user.directories.assets, category, request.body.filename);284 const file_path = path.join(request.user.directories.assets, category, request.body.filename);
283 console.debug('Request received to delete', category, file_path);285 console.info('Request received to delete', category, file_path);
284286
285 try {287 try {
286 // Delete if previous download failed288 // Delete if previous download failed
287 if (fs.existsSync(file_path)) {289 if (fs.existsSync(file_path)) {
288 fs.unlink(file_path, (err) => {290 fs.unlink(file_path, (err) => {
289 if (err) throw err;291 if (err) {
292 throw err;
293 }
290 });294 });
291 console.debug('Asset deleted.');295 console.info('Asset deleted.');
292 }296 }
293 else {297 else {
294 console.debug('Asset not found.');298 console.error('Asset not found.');
295 response.sendStatus(400);299 response.sendStatus(400);
296 }300 }
297 // Move into asset place301 // Move into asset place
298 response.sendStatus(200);302 response.sendStatus(200);
299 }303 }
300 catch (error) {304 catch (error) {
301 console.log(error);305 console.error(error);
302 response.sendStatus(500);306 response.sendStatus(500);
303 }307 }
304});308});
@@ -313,19 +317,24 @@ router.post('/delete', jsonParser, async (request, response) => {
313 * @returns {void}317 * @returns {void}
314 */318 */
315router.post('/character', jsonParser, async (request, response) => {319router.post('/character', jsonParser, async (request, response) => {
316 if (request.query.name === undefined) return response.sendStatus(400);320 if (request.query.name === undefined) {
321 return response.sendStatus(400);
322 }
323
317 // For backwards compatibility, don't reject invalid character names, just sanitize them324 // For backwards compatibility, don't reject invalid character names, just sanitize them
318 const name = sanitize(request.query.name.toString());325 const name = sanitize(request.query.name.toString());
319 const inputCategory = request.query.category;326 const inputCategory = request.query.category;
320327
321 // Check category328 // Check category
322 let category = null;329 let category = null;
323 for (let i of VALID_CATEGORIES)330 for (let i of VALID_CATEGORIES) {
324 if (i == inputCategory)331 if (i == inputCategory) {
325 category = i;332 category = i;
333 }
334 }
326335
327 if (category === null) {336 if (category === null) {
328 console.debug('Bad request: unsupported asset category.');337 console.error('Bad request: unsupported asset category.');
329 return response.sendStatus(400);338 return response.sendStatus(400);
330 }339 }
331340
@@ -339,7 +348,9 @@ router.post('/character', jsonParser, async (request, response) => {
339 if (category == 'live2d') {348 if (category == 'live2d') {
340 const folders = fs.readdirSync(folderPath, { withFileTypes: true });349 const folders = fs.readdirSync(folderPath, { withFileTypes: true });
341 for (const folderInfo of folders) {350 for (const folderInfo of folders) {
342 if (!folderInfo.isDirectory()) continue;351 if (!folderInfo.isDirectory()) {
352 continue;
353 }
343354
344 const modelFolder = folderInfo.name;355 const modelFolder = folderInfo.name;
345 const live2dModelPath = path.join(folderPath, modelFolder);356 const live2dModelPath = path.join(folderPath, modelFolder);
@@ -364,7 +375,7 @@ router.post('/character', jsonParser, async (request, response) => {
364 return response.send(output);375 return response.send(output);
365 }376 }
366 catch (err) {377 catch (err) {
367 console.log(err);378 console.error(err);
368 return response.sendStatus(500);379 return response.sendStatus(500);
369 }380 }
370});381});
src/endpoints/backends/chat-completions.js+49 -47
@@ -230,7 +230,7 @@ async function sendScaleRequest(request, response) {
230 }230 }
231231
232 const requestPrompt = convertTextCompletionPrompt(request.body.messages);232 const requestPrompt = convertTextCompletionPrompt(request.body.messages);
233 console.log('Scale request:', requestPrompt);233 console.debug('Scale request:', requestPrompt);
234234
235 try {235 try {
236 const controller = new AbortController();236 const controller = new AbortController();
@@ -249,18 +249,18 @@ async function sendScaleRequest(request, response) {
249 });249 });
250250
251 if (!generateResponse.ok) {251 if (!generateResponse.ok) {
252 console.log(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);252 console.error(`Scale API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
253 return response.status(500).send({ error: true });253 return response.status(500).send({ error: true });
254 }254 }
255255
256 /** @type {any} */256 /** @type {any} */
257 const generateResponseJson = await generateResponse.json();257 const generateResponseJson = await generateResponse.json();
258 console.log('Scale response:', generateResponseJson);258 console.debug('Scale response:', generateResponseJson);
259259
260 const reply = { choices: [{ 'message': { 'content': generateResponseJson.output } }] };260 const reply = { choices: [{ 'message': { 'content': generateResponseJson.output } }] };
261 return response.send(reply);261 return response.send(reply);
262 } catch (error) {262 } catch (error) {
263 console.log(error);263 console.error(error);
264 if (!response.headersSent) {264 if (!response.headersSent) {
265 return response.status(500).send({ error: true });265 return response.status(500).send({ error: true });
266 }266 }
@@ -277,7 +277,7 @@ async function sendMakerSuiteRequest(request, response) {
277 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);277 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
278278
279 if (!request.body.reverse_proxy && !apiKey) {279 if (!request.body.reverse_proxy && !apiKey) {
280 console.log('Google AI Studio API key is missing.');280 console.error('Google AI Studio API key is missing.');
281 return response.status(400).send({ error: true });281 return response.status(400).send({ error: true });
282 }282 }
283283
@@ -328,7 +328,7 @@ async function sendMakerSuiteRequest(request, response) {
328 }328 }
329329
330 const body = getGeminiBody();330 const body = getGeminiBody();
331 console.log('Google AI Studio request:', body);331 console.debug('Google AI Studio request:', body);
332332
333 try {333 try {
334 const controller = new AbortController();334 const controller = new AbortController();
@@ -355,14 +355,14 @@ async function sendMakerSuiteRequest(request, response) {
355 // Pipe remote SSE stream to Express response355 // Pipe remote SSE stream to Express response
356 forwardFetchResponse(generateResponse, response);356 forwardFetchResponse(generateResponse, response);
357 } catch (error) {357 } catch (error) {
358 console.log('Error forwarding streaming response:', error);358 console.error('Error forwarding streaming response:', error);
359 if (!response.headersSent) {359 if (!response.headersSent) {
360 return response.status(500).send({ error: true });360 return response.status(500).send({ error: true });
361 }361 }
362 }362 }
363 } else {363 } else {
364 if (!generateResponse.ok) {364 if (!generateResponse.ok) {
365 console.log(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);365 console.error(`Google AI Studio API returned error: ${generateResponse.status} ${generateResponse.statusText} ${await generateResponse.text()}`);
366 return response.status(generateResponse.status).send({ error: true });366 return response.status(generateResponse.status).send({ error: true });
367 }367 }
368368
@@ -380,7 +380,7 @@ async function sendMakerSuiteRequest(request, response) {
380 }380 }
381381
382 const responseContent = candidates[0].content ?? candidates[0].output;382 const responseContent = candidates[0].content ?? candidates[0].output;
383 console.log('Google AI Studio response:', responseContent);383 console.error('Google AI Studio response:', responseContent);
384384
385 if (Array.isArray(responseContent?.parts) && isThinking && !showThoughts) {385 if (Array.isArray(responseContent?.parts) && isThinking && !showThoughts) {
386 responseContent.parts = responseContent.parts.filter(part => !part.thought);386 responseContent.parts = responseContent.parts.filter(part => !part.thought);
@@ -389,7 +389,7 @@ async function sendMakerSuiteRequest(request, response) {
389 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join('\n\n');389 const responseText = typeof responseContent === 'string' ? responseContent : responseContent?.parts?.map(part => part.text)?.join('\n\n');
390 if (!responseText) {390 if (!responseText) {
391 let message = 'Google AI Studio Candidate text empty';391 let message = 'Google AI Studio Candidate text empty';
392 console.log(message, generateResponseJson);392 console.error(message, generateResponseJson);
393 return response.send({ error: { message } });393 return response.send({ error: { message } });
394 }394 }
395395
@@ -398,7 +398,7 @@ async function sendMakerSuiteRequest(request, response) {
398 return response.send(reply);398 return response.send(reply);
399 }399 }
400 } catch (error) {400 } catch (error) {
401 console.log('Error communicating with Google AI Studio API: ', error);401 console.error('Error communicating with Google AI Studio API: ', error);
402 if (!response.headersSent) {402 if (!response.headersSent) {
403 return response.status(500).send({ error: true });403 return response.status(500).send({ error: true });
404 }404 }
@@ -411,9 +411,12 @@ async function sendMakerSuiteRequest(request, response) {
411 * @param {express.Response} response Express response411 * @param {express.Response} response Express response
412 */412 */
413async function sendAI21Request(request, response) {413async function sendAI21Request(request, response) {
414 if (!request.body) return response.sendStatus(400);414 if (!request.body) {
415 return response.sendStatus(400);
416 }
417
415 const controller = new AbortController();418 const controller = new AbortController();
416 console.log(request.body.messages);419 console.debug(request.body.messages);
417 request.socket.removeAllListeners('close');420 request.socket.removeAllListeners('close');
418 request.socket.on('close', function () {421 request.socket.on('close', function () {
419 controller.abort();422 controller.abort();
@@ -439,7 +442,7 @@ async function sendAI21Request(request, response) {
439 signal: controller.signal,442 signal: controller.signal,
440 };443 };
441444
442 console.log('AI21 request:', body);445 console.debug('AI21 request:', body);
443446
444 try {447 try {
445 const generateResponse = await fetch(API_AI21 + '/chat/completions', options);448 const generateResponse = await fetch(API_AI21 + '/chat/completions', options);
@@ -448,16 +451,16 @@ async function sendAI21Request(request, response) {
448 } else {451 } else {
449 if (!generateResponse.ok) {452 if (!generateResponse.ok) {
450 const errorText = await generateResponse.text();453 const errorText = await generateResponse.text();
451 console.log(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);454 console.error(`AI21 API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
452 const errorJson = tryParse(errorText) ?? { error: true };455 const errorJson = tryParse(errorText) ?? { error: true };
453 return response.status(500).send(errorJson);456 return response.status(500).send(errorJson);
454 }457 }
455 const generateResponseJson = await generateResponse.json();458 const generateResponseJson = await generateResponse.json();
456 console.log('AI21 response:', generateResponseJson);459 console.debug('AI21 response:', generateResponseJson);
457 return response.send(generateResponseJson);460 return response.send(generateResponseJson);
458 }461 }
459 } catch (error) {462 } catch (error) {
460 console.log('Error communicating with AI21 API: ', error);463 console.error('Error communicating with AI21 API: ', error);
461 if (!response.headersSent) {464 if (!response.headersSent) {
462 response.send({ error: true });465 response.send({ error: true });
463 } else {466 } else {
@@ -476,7 +479,7 @@ async function sendMistralAIRequest(request, response) {
476 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);479 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);
477480
478 if (!apiKey) {481 if (!apiKey) {
479 console.log('MistralAI API key is missing.');482 console.error('MistralAI API key is missing.');
480 return response.status(400).send({ error: true });483 return response.status(400).send({ error: true });
481 }484 }
482485
@@ -517,7 +520,7 @@ async function sendMistralAIRequest(request, response) {
517 timeout: 0,520 timeout: 0,
518 };521 };
519522
520 console.log('MisralAI request:', requestBody);523 console.debug('MisralAI request:', requestBody);
521524
522 const generateResponse = await fetch(apiUrl + '/chat/completions', config);525 const generateResponse = await fetch(apiUrl + '/chat/completions', config);
523 if (request.body.stream) {526 if (request.body.stream) {
@@ -525,16 +528,16 @@ async function sendMistralAIRequest(request, response) {
525 } else {528 } else {
526 if (!generateResponse.ok) {529 if (!generateResponse.ok) {
527 const errorText = await generateResponse.text();530 const errorText = await generateResponse.text();
528 console.log(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);531 console.error(`MistralAI API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
529 const errorJson = tryParse(errorText) ?? { error: true };532 const errorJson = tryParse(errorText) ?? { error: true };
530 return response.status(500).send(errorJson);533 return response.status(500).send(errorJson);
531 }534 }
532 const generateResponseJson = await generateResponse.json();535 const generateResponseJson = await generateResponse.json();
533 console.log('MistralAI response:', generateResponseJson);536 console.debug('MistralAI response:', generateResponseJson);
534 return response.send(generateResponseJson);537 return response.send(generateResponseJson);
535 }538 }
536 } catch (error) {539 } catch (error) {
537 console.log('Error communicating with MistralAI API: ', error);540 console.error('Error communicating with MistralAI API: ', error);
538 if (!response.headersSent) {541 if (!response.headersSent) {
539 response.send({ error: true });542 response.send({ error: true });
540 } else {543 } else {
@@ -557,7 +560,7 @@ async function sendCohereRequest(request, response) {
557 });560 });
558561
559 if (!apiKey) {562 if (!apiKey) {
560 console.log('Cohere API key is missing.');563 console.error('Cohere API key is missing.');
561 return response.status(400).send({ error: true });564 return response.status(400).send({ error: true });
562 }565 }
563566
@@ -596,7 +599,7 @@ async function sendCohereRequest(request, response) {
596 requestBody.safety_mode = 'OFF';599 requestBody.safety_mode = 'OFF';
597 }600 }
598601
599 console.log('Cohere request:', requestBody);602 console.debug('Cohere request:', requestBody);
600603
601 const config = {604 const config = {
602 method: 'POST',605 method: 'POST',
@@ -618,16 +621,16 @@ async function sendCohereRequest(request, response) {
618 const generateResponse = await fetch(apiUrl, config);621 const generateResponse = await fetch(apiUrl, config);
619 if (!generateResponse.ok) {622 if (!generateResponse.ok) {
620 const errorText = await generateResponse.text();623 const errorText = await generateResponse.text();
621 console.log(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);624 console.error(`Cohere API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
622 const errorJson = tryParse(errorText) ?? { error: true };625 const errorJson = tryParse(errorText) ?? { error: true };
623 return response.status(500).send(errorJson);626 return response.status(500).send(errorJson);
624 }627 }
625 const generateResponseJson = await generateResponse.json();628 const generateResponseJson = await generateResponse.json();
626 console.log('Cohere response:', generateResponseJson);629 console.debug('Cohere response:', generateResponseJson);
627 return response.send(generateResponseJson);630 return response.send(generateResponseJson);
628 }631 }
629 } catch (error) {632 } catch (error) {
630 console.log('Error communicating with Cohere API: ', error);633 console.error('Error communicating with Cohere API: ', error);
631 if (!response.headersSent) {634 if (!response.headersSent) {
632 response.send({ error: true });635 response.send({ error: true });
633 } else {636 } else {
@@ -684,12 +687,12 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
684 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);687 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK);
685 headers = {};688 headers = {};
686 } else {689 } else {
687 console.log('This chat completion source is not supported yet.');690 console.error('This chat completion source is not supported yet.');
688 return response_getstatus_openai.status(400).send({ error: true });691 return response_getstatus_openai.status(400).send({ error: true });
689 }692 }
690693
691 if (!api_key_openai && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {694 if (!api_key_openai && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
692 console.log('Chat Completion API key is missing.');695 console.error('Chat Completion API key is missing.');
693 return response_getstatus_openai.status(400).send({ error: true });696 return response_getstatus_openai.status(400).send({ error: true });
694 }697 }
695698
@@ -724,23 +727,23 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
724 };727 };
725 });728 });
726729
727 console.log('Available OpenRouter models:', models);730 console.info('Available OpenRouter models:', models);
728 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {731 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MISTRALAI) {
729 const models = data?.data;732 const models = data?.data;
730 console.log(models);733 console.info(models);
731 } else {734 } else {
732 const models = data?.data;735 const models = data?.data;
733736
734 if (Array.isArray(models)) {737 if (Array.isArray(models)) {
735 const modelIds = models.filter(x => x && typeof x === 'object').map(x => x.id).sort();738 const modelIds = models.filter(x => x && typeof x === 'object').map(x => x.id).sort();
736 console.log('Available models:', modelIds);739 console.info('Available models:', modelIds);
737 } else {740 } else {
738 console.log('Chat Completion endpoint did not return a list of models.');741 console.warn('Chat Completion endpoint did not return a list of models.');
739 }742 }
740 }743 }
741 }744 }
742 else {745 else {
743 console.log('Chat Completion status check failed. Either Access Token is incorrect or API endpoint is down.');746 console.error('Chat Completion status check failed. Either Access Token is incorrect or API endpoint is down.');
744 response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } });747 response_getstatus_openai.send({ error: true, can_bypass: true, data: { data: [] } });
745 }748 }
746 } catch (e) {749 } catch (e) {
@@ -755,8 +758,7 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
755});758});
756759
757router.post('/bias', jsonParser, async function (request, response) {760router.post('/bias', jsonParser, async function (request, response) {
758 if (!request.body || !Array.isArray(request.body))761 if (!request.body || !Array.isArray(request.body)) return response.sendStatus(400);
759 return response.sendStatus(400);
760762
761 try {763 try {
762 const result = {};764 const result = {};
@@ -773,7 +775,7 @@ router.post('/bias', jsonParser, async function (request, response) {
773 const tokenizer = getSentencepiceTokenizer(model);775 const tokenizer = getSentencepiceTokenizer(model);
774 const instance = await tokenizer?.get();776 const instance = await tokenizer?.get();
775 if (!instance) {777 if (!instance) {
776 console.warn('Tokenizer not initialized:', model);778 console.error('Tokenizer not initialized:', model);
777 return response.send({});779 return response.send({});
778 }780 }
779 encodeFunction = (text) => new Uint32Array(instance.encodeIds(text));781 encodeFunction = (text) => new Uint32Array(instance.encodeIds(text));
@@ -922,7 +924,7 @@ router.post('/generate', jsonParser, function (request, response) {
922 mergeObjectWithYaml(headers, request.body.custom_include_headers);924 mergeObjectWithYaml(headers, request.body.custom_include_headers);
923925
924 if (request.body.custom_prompt_post_processing) {926 if (request.body.custom_prompt_post_processing) {
925 console.log('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);927 console.info('Applying custom prompt post-processing of type', request.body.custom_prompt_post_processing);
926 request.body.messages = postProcessPrompt(928 request.body.messages = postProcessPrompt(
927 request.body.messages,929 request.body.messages,
928 request.body.custom_prompt_post_processing,930 request.body.custom_prompt_post_processing,
@@ -967,12 +969,12 @@ router.post('/generate', jsonParser, function (request, response) {
967969
968 request.body.messages = postProcessPrompt(request.body.messages, 'deepseek', getPromptNames(request));970 request.body.messages = postProcessPrompt(request.body.messages, 'deepseek', getPromptNames(request));
969 } else {971 } else {
970 console.log('This chat completion source is not supported yet.');972 console.error('This chat completion source is not supported yet.');
971 return response.status(400).send({ error: true });973 return response.status(400).send({ error: true });
972 }974 }
973975
974 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {976 if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) {
975 console.log('OpenAI API key is missing.');977 console.error('OpenAI API key is missing.');
976 return response.status(400).send({ error: true });978 return response.status(400).send({ error: true });
977 }979 }
978980
@@ -1032,7 +1034,7 @@ router.post('/generate', jsonParser, function (request, response) {
1032 signal: controller.signal,1034 signal: controller.signal,
1033 };1035 };
10341036
1035 console.log(requestBody);1037 console.debug(requestBody);
10361038
1037 makeRequest(config, response, request);1039 makeRequest(config, response, request);
10381040
@@ -1049,7 +1051,7 @@ router.post('/generate', jsonParser, function (request, response) {
1049 const fetchResponse = await fetch(endpointUrl, config);1051 const fetchResponse = await fetch(endpointUrl, config);
10501052
1051 if (request.body.stream) {1053 if (request.body.stream) {
1052 console.log('Streaming request in progress');1054 console.info('Streaming request in progress');
1053 forwardFetchResponse(fetchResponse, response);1055 forwardFetchResponse(fetchResponse, response);
1054 return;1056 return;
1055 }1057 }
@@ -1058,10 +1060,10 @@ router.post('/generate', jsonParser, function (request, response) {
1058 /** @type {any} */1060 /** @type {any} */
1059 let json = await fetchResponse.json();1061 let json = await fetchResponse.json();
1060 response.send(json);1062 response.send(json);
1061 console.log(json);1063 console.debug(json);
1062 console.log(json?.choices?.[0]?.message);1064 console.debug(json?.choices?.[0]?.message);
1063 } else if (fetchResponse.status === 429 && retries > 0) {1065 } else if (fetchResponse.status === 429 && retries > 0) {
1064 console.log(`Out of quota, retrying in ${Math.round(timeout / 1000)}s`);1066 console.warn(`Out of quota, retrying in ${Math.round(timeout / 1000)}s`);
1065 setTimeout(() => {1067 setTimeout(() => {
1066 timeout *= 2;1068 timeout *= 2;
1067 makeRequest(config, response, request, retries - 1, timeout);1069 makeRequest(config, response, request, retries - 1, timeout);
@@ -1070,7 +1072,7 @@ router.post('/generate', jsonParser, function (request, response) {
1070 await handleErrorResponse(fetchResponse);1072 await handleErrorResponse(fetchResponse);
1071 }1073 }
1072 } catch (error) {1074 } catch (error) {
1073 console.log('Generation failed', error);1075 console.error('Generation failed', error);
1074 const message = error.code === 'ECONNREFUSED'1076 const message = error.code === 'ECONNREFUSED'
1075 ? `Connection refused: ${error.message}`1077 ? `Connection refused: ${error.message}`
1076 : error.message || 'Unknown error occurred';1078 : error.message || 'Unknown error occurred';
@@ -1092,7 +1094,7 @@ router.post('/generate', jsonParser, function (request, response) {
10921094
1093 const message = errorResponse.statusText || 'Unknown error occurred';1095 const message = errorResponse.statusText || 'Unknown error occurred';
1094 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';1096 const quota_error = errorResponse.status === 429 && errorData?.error?.type === 'insufficient_quota';
1095 console.log('Chat completion request error: ', message, responseText);1097 console.error('Chat completion request error: ', message, responseText);
10961098
1097 if (!response.headersSent) {1099 if (!response.headersSent) {
1098 response.send({ error: { message }, quota_error: quota_error });1100 response.send({ error: { message }, quota_error: quota_error });
src/endpoints/backends/kobold.js+15 -14
@@ -22,17 +22,17 @@ router.post('/generate', jsonParser, async function (request, response_generate)
22 request.socket.on('close', async function () {22 request.socket.on('close', async function () {
23 if (request.body.can_abort && !response_generate.writableEnded) {23 if (request.body.can_abort && !response_generate.writableEnded) {
24 try {24 try {
25 console.log('Aborting Kobold generation...');25 console.info('Aborting Kobold generation...');
26 // send abort signal to koboldcpp26 // send abort signal to koboldcpp
27 const abortResponse = await fetch(`${request.body.api_server}/extra/abort`, {27 const abortResponse = await fetch(`${request.body.api_server}/extra/abort`, {
28 method: 'POST',28 method: 'POST',
29 });29 });
3030
31 if (!abortResponse.ok) {31 if (!abortResponse.ok) {
32 console.log('Error sending abort request to Kobold:', abortResponse.status);32 console.error('Error sending abort request to Kobold:', abortResponse.status);
33 }33 }
34 } catch (error) {34 } catch (error) {
35 console.log(error);35 console.error(error);
36 }36 }
37 }37 }
38 controller.abort();38 controller.abort();
@@ -81,7 +81,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
81 }81 }
82 }82 }
8383
84 console.log(this_settings);84 console.debug(this_settings);
85 const args = {85 const args = {
86 body: JSON.stringify(this_settings),86 body: JSON.stringify(this_settings),
87 headers: Object.assign(87 headers: Object.assign(
@@ -105,7 +105,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
105 } else {105 } else {
106 if (!response.ok) {106 if (!response.ok) {
107 const errorText = await response.text();107 const errorText = await response.text();
108 console.log(`Kobold returned error: ${response.status} ${response.statusText} ${errorText}`);108 console.error(`Kobold returned error: ${response.status} ${response.statusText} ${errorText}`);
109109
110 try {110 try {
111 const errorJson = JSON.parse(errorText);111 const errorJson = JSON.parse(errorText);
@@ -117,7 +117,7 @@ router.post('/generate', jsonParser, async function (request, response_generate)
117 }117 }
118118
119 const data = await response.json();119 const data = await response.json();
120 console.log('Endpoint response:', data);120 console.debug('Endpoint response:', data);
121 return response_generate.send(data);121 return response_generate.send(data);
122 }122 }
123 } catch (error) {123 } catch (error) {
@@ -125,19 +125,19 @@ router.post('/generate', jsonParser, async function (request, response_generate)
125 switch (error?.status) {125 switch (error?.status) {
126 case 403:126 case 403:
127 case 503: // retry in case of temporary service issue, possibly caused by a queue failure?127 case 503: // retry in case of temporary service issue, possibly caused by a queue failure?
128 console.debug(`KoboldAI is busy. Retry attempt ${i + 1} of ${MAX_RETRIES}...`);128 console.warn(`KoboldAI is busy. Retry attempt ${i + 1} of ${MAX_RETRIES}...`);
129 await delay(delayAmount);129 await delay(delayAmount);
130 break;130 break;
131 default:131 default:
132 if ('status' in error) {132 if ('status' in error) {
133 console.log('Status Code from Kobold:', error.status);133 console.error('Status Code from Kobold:', error.status);
134 }134 }
135 return response_generate.send({ error: true });135 return response_generate.send({ error: true });
136 }136 }
137 }137 }
138 }138 }
139139
140 console.log('Max retries exceeded. Giving up.');140 console.error('Max retries exceeded. Giving up.');
141 return response_generate.send({ error: true });141 return response_generate.send({ error: true });
142});142});
143143
@@ -193,16 +193,17 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
193 const server = request.body.server;193 const server = request.body.server;
194194
195 if (!server) {195 if (!server) {
196 console.log('Server is not set');196 console.error('Server is not set');
197 return response.sendStatus(400);197 return response.sendStatus(400);
198 }198 }
199199
200 if (!request.file) {200 if (!request.file) {
201 console.log('No audio file found');201 console.error('No audio file found');
202 return response.sendStatus(400);202 return response.sendStatus(400);
203 }203 }
204204
205 console.log('Transcribing audio with KoboldCpp', server);205 console.info('Transcribing audio with KoboldCpp');
206 console.debug(server);
206207
207 const fileBase64 = fs.readFileSync(request.file.path).toString('base64');208 const fileBase64 = fs.readFileSync(request.file.path).toString('base64');
208 fs.rmSync(request.file.path);209 fs.rmSync(request.file.path);
@@ -226,12 +227,12 @@ router.post('/transcribe-audio', urlencodedParser, async function (request, resp
226227
227 if (!result.ok) {228 if (!result.ok) {
228 const text = await result.text();229 const text = await result.text();
229 console.log('KoboldCpp request failed', result.statusText, text);230 console.error('KoboldCpp request failed', result.statusText, text);
230 return response.status(500).send(text);231 return response.status(500).send(text);
231 }232 }
232233
233 const data = await result.json();234 const data = await result.json();
234 console.log('KoboldCpp transcription response', data);235 console.debug('KoboldCpp transcription response', data);
235 return response.json(data);236 return response.json(data);
236 } catch (error) {237 } catch (error) {
237 console.error('KoboldCpp transcription failed', error);238 console.error('KoboldCpp transcription failed', error);
src/endpoints/backends/scale-alt.js+5 -5
@@ -13,7 +13,7 @@ router.post('/generate', jsonParser, async function (request, response) {
13 const cookie = readSecret(request.user.directories, SECRET_KEYS.SCALE_COOKIE);13 const cookie = readSecret(request.user.directories, SECRET_KEYS.SCALE_COOKIE);
1414
15 if (!cookie) {15 if (!cookie) {
16 console.log('No Scale cookie found');16 console.error('No Scale cookie found');
17 return response.sendStatus(400);17 return response.sendStatus(400);
18 }18 }
1919
@@ -62,7 +62,7 @@ router.post('/generate', jsonParser, async function (request, response) {
62 },62 },
63 };63 };
6464
65 console.log('Scale request:', body);65 console.debug('Scale request:', body);
6666
67 const result = await fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {67 const result = await fetch('https://dashboard.scale.com/spellbook/api/trpc/v2.variant.run', {
68 method: 'POST',68 method: 'POST',
@@ -75,7 +75,7 @@ router.post('/generate', jsonParser, async function (request, response) {
7575
76 if (!result.ok) {76 if (!result.ok) {
77 const text = await result.text();77 const text = await result.text();
78 console.log('Scale request failed', result.statusText, text);78 console.error('Scale request failed', result.statusText, text);
79 return response.status(500).send({ error: { message: result.statusText } });79 return response.status(500).send({ error: { message: result.statusText } });
80 }80 }
8181
@@ -83,7 +83,7 @@ router.post('/generate', jsonParser, async function (request, response) {
83 const data = await result.json();83 const data = await result.json();
84 const output = data?.result?.data?.json?.outputs?.[0] || '';84 const output = data?.result?.data?.json?.outputs?.[0] || '';
8585
86 console.log('Scale response:', data);86 console.debug('Scale response:', data);
8787
88 if (!output) {88 if (!output) {
89 console.warn('Scale response is empty');89 console.warn('Scale response is empty');
@@ -92,7 +92,7 @@ router.post('/generate', jsonParser, async function (request, response) {
9292
93 return response.json({ output });93 return response.json({ output });
94 } catch (error) {94 } catch (error) {
95 console.log(error);95 console.error(error);
96 return response.sendStatus(500);96 return response.sendStatus(500);
97 }97 }
98});98});
src/endpoints/backends/text-completions.js+49 -48
@@ -58,12 +58,12 @@ async function parseOllamaStream(jsonStream, request, response) {
58 });58 });
5959
60 jsonStream.body.on('end', () => {60 jsonStream.body.on('end', () => {
61 console.log('Streaming request finished');61 console.info('Streaming request finished');
62 response.write('data: [DONE]\n\n');62 response.write('data: [DONE]\n\n');
63 response.end();63 response.end();
64 });64 });
65 } catch (error) {65 } catch (error) {
66 console.log('Error forwarding streaming response:', error);66 console.error('Error forwarding streaming response:', error);
67 if (!response.headersSent) {67 if (!response.headersSent) {
68 return response.status(500).send({ error: true });68 return response.status(500).send({ error: true });
69 } else {69 } else {
@@ -79,16 +79,16 @@ async function parseOllamaStream(jsonStream, request, response) {
79 */79 */
80async function abortKoboldCppRequest(url) {80async function abortKoboldCppRequest(url) {
81 try {81 try {
82 console.log('Aborting Kobold generation...');82 console.info('Aborting Kobold generation...');
83 const abortResponse = await fetch(`${url}/api/extra/abort`, {83 const abortResponse = await fetch(`${url}/api/extra/abort`, {
84 method: 'POST',84 method: 'POST',
85 });85 });
8686
87 if (!abortResponse.ok) {87 if (!abortResponse.ok) {
88 console.log('Error sending abort request to Kobold:', abortResponse.status, abortResponse.statusText);88 console.error('Error sending abort request to Kobold:', abortResponse.status, abortResponse.statusText);
89 }89 }
90 } catch (error) {90 } catch (error) {
91 console.log(error);91 console.error(error);
92 }92 }
93}93}
9494
@@ -101,7 +101,8 @@ router.post('/status', jsonParser, async function (request, response) {
101 request.body.api_server = request.body.api_server.replace('localhost', '127.0.0.1');101 request.body.api_server = request.body.api_server.replace('localhost', '127.0.0.1');
102 }102 }
103103
104 console.log('Trying to connect to API:', request.body);104 console.info('Trying to connect to API');
105 console.debug(request.body);
105 const baseUrl = trimV1(request.body.api_server);106 const baseUrl = trimV1(request.body.api_server);
106107
107 const args = {108 const args = {
@@ -123,6 +124,7 @@ router.post('/status', jsonParser, async function (request, response) {
123 case TEXTGEN_TYPES.LLAMACPP:124 case TEXTGEN_TYPES.LLAMACPP:
124 case TEXTGEN_TYPES.INFERMATICAI:125 case TEXTGEN_TYPES.INFERMATICAI:
125 case TEXTGEN_TYPES.OPENROUTER:126 case TEXTGEN_TYPES.OPENROUTER:
127 case TEXTGEN_TYPES.FEATHERLESS:
126 url += '/v1/models';128 url += '/v1/models';
127 break;129 break;
128 case TEXTGEN_TYPES.DREAMGEN:130 case TEXTGEN_TYPES.DREAMGEN:
@@ -140,9 +142,6 @@ router.post('/status', jsonParser, async function (request, response) {
140 case TEXTGEN_TYPES.OLLAMA:142 case TEXTGEN_TYPES.OLLAMA:
141 url += '/api/tags';143 url += '/api/tags';
142 break;144 break;
143 case TEXTGEN_TYPES.FEATHERLESS:
144 url += '/v1/models';
145 break;
146 case TEXTGEN_TYPES.HUGGINGFACE:145 case TEXTGEN_TYPES.HUGGINGFACE:
147 url += '/info';146 url += '/info';
148 break;147 break;
@@ -152,7 +151,7 @@ router.post('/status', jsonParser, async function (request, response) {
152 const isPossiblyLmStudio = modelsReply.headers.get('x-powered-by') === 'Express';151 const isPossiblyLmStudio = modelsReply.headers.get('x-powered-by') === 'Express';
153152
154 if (!modelsReply.ok) {153 if (!modelsReply.ok) {
155 console.log('Models endpoint is offline.');154 console.error('Models endpoint is offline.');
156 return response.sendStatus(400);155 return response.sendStatus(400);
157 }156 }
158157
@@ -173,12 +172,12 @@ router.post('/status', jsonParser, async function (request, response) {
173 }172 }
174173
175 if (!Array.isArray(data.data)) {174 if (!Array.isArray(data.data)) {
176 console.log('Models response is not an array.');175 console.error('Models response is not an array.');
177 return response.sendStatus(400);176 return response.sendStatus(400);
178 }177 }
179178
180 const modelIds = data.data.map(x => x.id);179 const modelIds = data.data.map(x => x.id);
181 console.log('Models available:', modelIds);180 console.info('Models available:', modelIds);
182181
183 // Set result to the first model ID182 // Set result to the first model ID
184 result = modelIds[0] || 'Valid';183 result = modelIds[0] || 'Valid';
@@ -191,7 +190,7 @@ router.post('/status', jsonParser, async function (request, response) {
191 if (modelInfoReply.ok) {190 if (modelInfoReply.ok) {
192 /** @type {any} */191 /** @type {any} */
193 const modelInfo = await modelInfoReply.json();192 const modelInfo = await modelInfoReply.json();
194 console.log('Ooba model info:', modelInfo);193 console.debug('Ooba model info:', modelInfo);
195194
196 const modelName = modelInfo?.model_name;195 const modelName = modelInfo?.model_name;
197 result = modelName || result;196 result = modelName || result;
@@ -208,7 +207,7 @@ router.post('/status', jsonParser, async function (request, response) {
208 if (modelInfoReply.ok) {207 if (modelInfoReply.ok) {
209 /** @type {any} */208 /** @type {any} */
210 const modelInfo = await modelInfoReply.json();209 const modelInfo = await modelInfoReply.json();
211 console.log('Tabby model info:', modelInfo);210 console.debug('Tabby model info:', modelInfo);
212211
213 const modelName = modelInfo?.id;212 const modelName = modelInfo?.id;
214 result = modelName || result;213 result = modelName || result;
@@ -230,7 +229,9 @@ router.post('/status', jsonParser, async function (request, response) {
230});229});
231230
232router.post('/props', jsonParser, async function (request, response) {231router.post('/props', jsonParser, async function (request, response) {
233 if (!request.body.api_server) return response.sendStatus(400);232 if (!request.body.api_server) {
233 return response.sendStatus(400);
234 }
234235
235 try {236 try {
236 const baseUrl = trimV1(request.body.api_server);237 const baseUrl = trimV1(request.body.api_server);
@@ -255,7 +256,7 @@ router.post('/props', jsonParser, async function (request, response) {
255 props['chat_template'] = props['chat_template'].slice(0, -1) + '\n';256 props['chat_template'] = props['chat_template'].slice(0, -1) + '\n';
256 }257 }
257 props['chat_template_hash'] = createHash('sha256').update(props['chat_template']).digest('hex');258 props['chat_template_hash'] = createHash('sha256').update(props['chat_template']).digest('hex');
258 console.log(`Model properties: ${JSON.stringify(props)}`);259 console.debug(`Model properties: ${JSON.stringify(props)}`);
259 return response.send(props);260 return response.send(props);
260 } catch (error) {261 } catch (error) {
261 console.error(error);262 console.error(error);
@@ -264,7 +265,9 @@ router.post('/props', jsonParser, async function (request, response) {
264});265});
265266
266router.post('/generate', jsonParser, async function (request, response) {267router.post('/generate', jsonParser, async function (request, response) {
267 if (!request.body) return response.sendStatus(400);268 if (!request.body) {
269 return response.sendStatus(400);
270 }
268271
269 try {272 try {
270 if (request.body.api_server.indexOf('localhost') !== -1) {273 if (request.body.api_server.indexOf('localhost') !== -1) {
@@ -273,7 +276,7 @@ router.post('/generate', jsonParser, async function (request, response) {
273276
274 const apiType = request.body.api_type;277 const apiType = request.body.api_type;
275 const baseUrl = request.body.api_server;278 const baseUrl = request.body.api_server;
276 console.log(request.body);279 console.debug(request.body);
277280
278 const controller = new AbortController();281 const controller = new AbortController();
279 request.socket.removeAllListeners('close');282 request.socket.removeAllListeners('close');
@@ -399,7 +402,7 @@ router.post('/generate', jsonParser, async function (request, response) {
399 if (completionsReply.ok) {402 if (completionsReply.ok) {
400 /** @type {any} */403 /** @type {any} */
401 const data = await completionsReply.json();404 const data = await completionsReply.json();
402 console.log('Endpoint response:', data);405 console.debug('Endpoint response:', data);
403406
404 // Map InfermaticAI response to OAI completions format407 // Map InfermaticAI response to OAI completions format
405 if (apiType === TEXTGEN_TYPES.INFERMATICAI) {408 if (apiType === TEXTGEN_TYPES.INFERMATICAI) {
@@ -411,24 +414,20 @@ router.post('/generate', jsonParser, async function (request, response) {
411 const text = await completionsReply.text();414 const text = await completionsReply.text();
412 const errorBody = { error: true, status: completionsReply.status, response: text };415 const errorBody = { error: true, status: completionsReply.status, response: text };
413416
414 if (!response.headersSent) {417 return !response.headersSent
415 return response.send(errorBody);418 ? response.send(errorBody)
416 }419 : response.end();
417
418 return response.end();
419 }420 }
420 }421 }
421 } catch (error) {422 } catch (error) {
422 const status = error?.status ?? error?.code ?? 'UNKNOWN';423 const status = error?.status ?? error?.code ?? 'UNKNOWN';
423 const text = error?.error ?? error?.statusText ?? error?.message ?? 'Unknown error on /generate endpoint';424 const text = error?.error ?? error?.statusText ?? error?.message ?? 'Unknown error on /generate endpoint';
424 let value = { error: true, status: status, response: text };425 let value = { error: true, status: status, response: text };
425 console.log('Endpoint error:', error);426 console.error('Endpoint error:', error);
426
427 if (!response.headersSent) {
428 return response.send(value);
429 }
430427
431 return response.end();428 return !response.headersSent
429 ? response.send(value)
430 : response.end();
432 }431 }
433});432});
434433
@@ -436,7 +435,9 @@ const ollama = express.Router();
436435
437ollama.post('/download', jsonParser, async function (request, response) {436ollama.post('/download', jsonParser, async function (request, response) {
438 try {437 try {
439 if (!request.body.name || !request.body.api_server) return response.sendStatus(400);438 if (!request.body.name || !request.body.api_server) {
439 return response.sendStatus(400);
440 }
440441
441 const name = request.body.name;442 const name = request.body.name;
442 const url = String(request.body.api_server).replace(/\/$/, '');443 const url = String(request.body.api_server).replace(/\/$/, '');
@@ -451,7 +452,7 @@ ollama.post('/download', jsonParser, async function (request, response) {
451 });452 });
452453
453 if (!fetchResponse.ok) {454 if (!fetchResponse.ok) {
454 console.log('Download error:', fetchResponse.status, fetchResponse.statusText);455 console.error('Download error:', fetchResponse.status, fetchResponse.statusText);
455 return response.status(fetchResponse.status).send({ error: true });456 return response.status(fetchResponse.status).send({ error: true });
456 }457 }
457458
@@ -468,7 +469,7 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
468 return response.sendStatus(400);469 return response.sendStatus(400);
469 }470 }
470471
471 console.log('Ollama caption request:', request.body);472 console.debug('Ollama caption request:', request.body);
472 const baseUrl = trimV1(request.body.server_url);473 const baseUrl = trimV1(request.body.server_url);
473474
474 const fetchResponse = await fetch(`${baseUrl}/api/generate`, {475 const fetchResponse = await fetch(`${baseUrl}/api/generate`, {
@@ -483,18 +484,18 @@ ollama.post('/caption-image', jsonParser, async function (request, response) {
483 });484 });
484485
485 if (!fetchResponse.ok) {486 if (!fetchResponse.ok) {
486 console.log('Ollama caption error:', fetchResponse.status, fetchResponse.statusText);487 console.error('Ollama caption error:', fetchResponse.status, fetchResponse.statusText);
487 return response.status(500).send({ error: true });488 return response.status(500).send({ error: true });
488 }489 }
489490
490 /** @type {any} */491 /** @type {any} */
491 const data = await fetchResponse.json();492 const data = await fetchResponse.json();
492 console.log('Ollama caption response:', data);493 console.debug('Ollama caption response:', data);
493494
494 const caption = data?.response || '';495 const caption = data?.response || '';
495496
496 if (!caption) {497 if (!caption) {
497 console.log('Ollama caption is empty.');498 console.error('Ollama caption is empty.');
498 return response.status(500).send({ error: true });499 return response.status(500).send({ error: true });
499 }500 }
500501
@@ -513,7 +514,7 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
513 return response.sendStatus(400);514 return response.sendStatus(400);
514 }515 }
515516
516 console.log('LlamaCpp caption request:', request.body);517 console.debug('LlamaCpp caption request:', request.body);
517 const baseUrl = trimV1(request.body.server_url);518 const baseUrl = trimV1(request.body.server_url);
518519
519 const fetchResponse = await fetch(`${baseUrl}/completion`, {520 const fetchResponse = await fetch(`${baseUrl}/completion`, {
@@ -529,18 +530,18 @@ llamacpp.post('/caption-image', jsonParser, async function (request, response) {
529 });530 });
530531
531 if (!fetchResponse.ok) {532 if (!fetchResponse.ok) {
532 console.log('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);533 console.error('LlamaCpp caption error:', fetchResponse.status, fetchResponse.statusText);
533 return response.status(500).send({ error: true });534 return response.status(500).send({ error: true });
534 }535 }
535536
536 /** @type {any} */537 /** @type {any} */
537 const data = await fetchResponse.json();538 const data = await fetchResponse.json();
538 console.log('LlamaCpp caption response:', data);539 console.debug('LlamaCpp caption response:', data);
539540
540 const caption = data?.content || '';541 const caption = data?.content || '';
541542
542 if (!caption) {543 if (!caption) {
543 console.log('LlamaCpp caption is empty.');544 console.error('LlamaCpp caption is empty.');
544 return response.status(500).send({ error: true });545 return response.status(500).send({ error: true });
545 }546 }
546547
@@ -558,7 +559,7 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
558 return response.sendStatus(400);559 return response.sendStatus(400);
559 }560 }
560561
561 console.log('LlamaCpp props request:', request.body);562 console.debug('LlamaCpp props request:', request.body);
562 const baseUrl = trimV1(request.body.server_url);563 const baseUrl = trimV1(request.body.server_url);
563564
564 const fetchResponse = await fetch(`${baseUrl}/props`, {565 const fetchResponse = await fetch(`${baseUrl}/props`, {
@@ -566,12 +567,12 @@ llamacpp.post('/props', jsonParser, async function (request, response) {
566 });567 });
567568
568 if (!fetchResponse.ok) {569 if (!fetchResponse.ok) {
569 console.log('LlamaCpp props error:', fetchResponse.status, fetchResponse.statusText);570 console.error('LlamaCpp props error:', fetchResponse.status, fetchResponse.statusText);
570 return response.status(500).send({ error: true });571 return response.status(500).send({ error: true });
571 }572 }
572573
573 const data = await fetchResponse.json();574 const data = await fetchResponse.json();
574 console.log('LlamaCpp props response:', data);575 console.debug('LlamaCpp props response:', data);
575576
576 return response.send(data);577 return response.send(data);
577578
@@ -590,7 +591,7 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
590 return response.sendStatus(400);591 return response.sendStatus(400);
591 }592 }
592593
593 console.log('LlamaCpp slots request:', request.body);594 console.debug('LlamaCpp slots request:', request.body);
594 const baseUrl = trimV1(request.body.server_url);595 const baseUrl = trimV1(request.body.server_url);
595596
596 let fetchResponse;597 let fetchResponse;
@@ -616,12 +617,12 @@ llamacpp.post('/slots', jsonParser, async function (request, response) {
616 }617 }
617618
618 if (!fetchResponse.ok) {619 if (!fetchResponse.ok) {
619 console.log('LlamaCpp slots error:', fetchResponse.status, fetchResponse.statusText);620 console.error('LlamaCpp slots error:', fetchResponse.status, fetchResponse.statusText);
620 return response.status(500).send({ error: true });621 return response.status(500).send({ error: true });
621 }622 }
622623
623 const data = await fetchResponse.json();624 const data = await fetchResponse.json();
624 console.log('LlamaCpp slots response:', data);625 console.debug('LlamaCpp slots response:', data);
625626
626 return response.send(data);627 return response.send(data);
627628
@@ -659,14 +660,14 @@ tabby.post('/download', jsonParser, async function (request, response) {
659 return response.status(403).send({ error: true });660 return response.status(403).send({ error: true });
660 }661 }
661 } else {662 } else {
662 console.log('API Permission error:', permissionResponse.status, permissionResponse.statusText);663 console.error('API Permission error:', permissionResponse.status, permissionResponse.statusText);
663 return response.status(permissionResponse.status).send({ error: true });664 return response.status(permissionResponse.status).send({ error: true });
664 }665 }
665666
666 const fetchResponse = await fetch(`${baseUrl}/v1/download`, args);667 const fetchResponse = await fetch(`${baseUrl}/v1/download`, args);
667668
668 if (!fetchResponse.ok) {669 if (!fetchResponse.ok) {
669 console.log('Download error:', fetchResponse.status, fetchResponse.statusText);670 console.error('Download error:', fetchResponse.status, fetchResponse.statusText);
670 return response.status(fetchResponse.status).send({ error: true });671 return response.status(fetchResponse.status).send({ error: true });
671 }672 }
672673
src/endpoints/backgrounds.js+3 -3
@@ -26,7 +26,7 @@ router.post('/delete', jsonParser, function (request, response) {
26 const fileName = path.join(request.user.directories.backgrounds, sanitize(request.body.bg));26 const fileName = path.join(request.user.directories.backgrounds, sanitize(request.body.bg));
2727
28 if (!fs.existsSync(fileName)) {28 if (!fs.existsSync(fileName)) {
29 console.log('BG file not found');29 console.error('BG file not found');
30 return response.sendStatus(400);30 return response.sendStatus(400);
31 }31 }
3232
@@ -42,12 +42,12 @@ router.post('/rename', jsonParser, function (request, response) {
42 const newFileName = path.join(request.user.directories.backgrounds, sanitize(request.body.new_bg));42 const newFileName = path.join(request.user.directories.backgrounds, sanitize(request.body.new_bg));
4343
44 if (!fs.existsSync(oldFileName)) {44 if (!fs.existsSync(oldFileName)) {
45 console.log('BG file not found');45 console.error('BG file not found');
46 return response.sendStatus(400);46 return response.sendStatus(400);
47 }47 }
4848
49 if (fs.existsSync(newFileName)) {49 if (fs.existsSync(newFileName)) {
50 console.log('New BG file already exists');50 console.error('New BG file already exists');
51 return response.sendStatus(400);51 return response.sendStatus(400);
52 }52 }
5353
src/endpoints/caption.js+4 -4
@@ -2,10 +2,10 @@ import express from 'express';
2import { jsonParser } from '../express-common.js';2import { jsonParser } from '../express-common.js';
3import { getPipeline, getRawImage } from '../transformers.js';3import { getPipeline, getRawImage } from '../transformers.js';
44
5const TASK = 'image-to-text';
6
7export const router = express.Router();5export const router = express.Router();
86
7const TASK = 'image-to-text';
8
9router.post('/', jsonParser, async (req, res) => {9router.post('/', jsonParser, async (req, res) => {
10 try {10 try {
11 const { image } = req.body;11 const { image } = req.body;
@@ -13,14 +13,14 @@ router.post('/', jsonParser, async (req, res) => {
13 const rawImage = await getRawImage(image);13 const rawImage = await getRawImage(image);
1414
15 if (!rawImage) {15 if (!rawImage) {
16 console.log('Failed to parse captioned image');16 console.error('Failed to parse captioned image');
17 return res.sendStatus(400);17 return res.sendStatus(400);
18 }18 }
1919
20 const pipe = await getPipeline(TASK);20 const pipe = await getPipeline(TASK);
21 const result = await pipe(rawImage);21 const result = await pipe(rawImage);
22 const text = result[0].generated_text;22 const text = result[0].generated_text;
23 console.log('Image caption:', text);23 console.debug('Image caption:', text);
2424
25 return res.json({ caption: text });25 return res.json({ caption: text });
26 } catch (error) {26 } catch (error) {
src/endpoints/characters.js+28 -29
@@ -90,7 +90,7 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
90 writeFileAtomicSync(outputImagePath, outputImage);90 writeFileAtomicSync(outputImagePath, outputImage);
91 return true;91 return true;
92 } catch (err) {92 } catch (err) {
93 console.log(err);93 console.error(err);
94 return false;94 return false;
95 }95 }
96}96}
@@ -159,7 +159,7 @@ async function tryReadImage(imgPath, crop) {
159 }159 }
160 // If it's an unsupported type of image (APNG) - just read the file as buffer160 // If it's an unsupported type of image (APNG) - just read the file as buffer
161 catch (error) {161 catch (error) {
162 console.log(`Failed to read image: ${imgPath}`, error);162 console.error(`Failed to read image: ${imgPath}`, error);
163 return fs.readFileSync(imgPath);163 return fs.readFileSync(imgPath);
164 }164 }
165}165}
@@ -222,12 +222,12 @@ const processCharacter = async (item, directories) => {
222 return character;222 return character;
223 }223 }
224 catch (err) {224 catch (err) {
225 console.log(`Could not process character: ${item}`);225 console.error(`Could not process character: ${item}`);
226226
227 if (err instanceof SyntaxError) {227 if (err instanceof SyntaxError) {
228 console.log(`${item} does not contain a valid JSON object.`);228 console.error(`${item} does not contain a valid JSON object.`);
229 } else {229 } else {
230 console.log('An unexpected error occurred: ', err);230 console.error('An unexpected error occurred: ', err);
231 }231 }
232232
233 return {233 return {
@@ -315,7 +315,7 @@ function readFromV2(char) {
315 };315 };
316316
317 _.forEach(fieldMappings, (v2Path, charField) => {317 _.forEach(fieldMappings, (v2Path, charField) => {
318 //console.log(`Migrating field: ${charField} from ${v2Path}`);318 //console.info(`Migrating field: ${charField} from ${v2Path}`);
319 const v2Value = _.get(char.data, v2Path);319 const v2Value = _.get(char.data, v2Path);
320 if (_.isUndefined(v2Value)) {320 if (_.isUndefined(v2Value)) {
321 let defaultValue = undefined;321 let defaultValue = undefined;
@@ -330,15 +330,15 @@ function readFromV2(char) {
330 }330 }
331331
332 if (!_.isUndefined(defaultValue)) {332 if (!_.isUndefined(defaultValue)) {
333 //console.debug(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);333 //console.warn(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);
334 char[charField] = defaultValue;334 char[charField] = defaultValue;
335 } else {335 } else {
336 console.debug(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);336 console.warn(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);
337 return;337 return;
338 }338 }
339 }339 }
340 if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {340 if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {
341 console.debug(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);341 console.warn(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);
342 }342 }
343 char[charField] = v2Value;343 char[charField] = v2Value;
344 });344 });
@@ -435,7 +435,7 @@ function charaFormatData(data, directories) {
435 }435 }
436436
437 } catch {437 } catch {
438 console.debug(`Failed to read world info file: ${data.world}. Character book will not be available.`);438 console.warn(`Failed to read world info file: ${data.world}. Character book will not be available.`);
439 }439 }
440 }440 }
441441
@@ -445,7 +445,7 @@ function charaFormatData(data, directories) {
445 // Deep merge the extensions object445 // Deep merge the extensions object
446 _.set(char, 'data.extensions', deepMerge(char.data.extensions, extensions));446 _.set(char, 'data.extensions', deepMerge(char.data.extensions, extensions));
447 } catch {447 } catch {
448 console.debug(`Failed to parse extensions JSON: ${data.extensions}`);448 console.warn(`Failed to parse extensions JSON: ${data.extensions}`);
449 }449 }
450 }450 }
451451
@@ -519,7 +519,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) {
519 const fileText = fs.readFileSync(uploadPath, 'utf8');519 const fileText = fs.readFileSync(uploadPath, 'utf8');
520 fs.rmSync(uploadPath);520 fs.rmSync(uploadPath);
521 const yamlData = yaml.parse(fileText);521 const yamlData = yaml.parse(fileText);
522 console.log('Importing from YAML');522 console.info('Importing from YAML');
523 yamlData.name = sanitize(yamlData.name);523 yamlData.name = sanitize(yamlData.name);
524 const fileName = preservedFileName || getPngName(yamlData.name, context.request.user.directories);524 const fileName = preservedFileName || getPngName(yamlData.name, context.request.user.directories);
525 let char = convertToV2({525 let char = convertToV2({
@@ -552,7 +552,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) {
552async function importFromCharX(uploadPath, { request }, preservedFileName) {552async function importFromCharX(uploadPath, { request }, preservedFileName) {
553 const data = fs.readFileSync(uploadPath).buffer;553 const data = fs.readFileSync(uploadPath).buffer;
554 fs.rmSync(uploadPath);554 fs.rmSync(uploadPath);
555 console.log('Importing from CharX');555 console.info('Importing from CharX');
556 const cardBuffer = await extractFileFromZipBuffer(data, 'card.json');556 const cardBuffer = await extractFileFromZipBuffer(data, 'card.json');
557557
558 if (!cardBuffer) {558 if (!cardBuffer) {
@@ -601,7 +601,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
601 let jsonData = JSON.parse(data);601 let jsonData = JSON.parse(data);
602602
603 if (jsonData.spec !== undefined) {603 if (jsonData.spec !== undefined) {
604 console.log(`Importing from ${jsonData.spec} json`);604 console.info(`Importing from ${jsonData.spec} json`);
605 importRisuSprites(request.user.directories, jsonData);605 importRisuSprites(request.user.directories, jsonData);
606 unsetFavFlag(jsonData);606 unsetFavFlag(jsonData);
607 jsonData = readFromV2(jsonData);607 jsonData = readFromV2(jsonData);
@@ -611,7 +611,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
611 const result = await writeCharacterData(defaultAvatarPath, char, pngName, request);611 const result = await writeCharacterData(defaultAvatarPath, char, pngName, request);
612 return result ? pngName : '';612 return result ? pngName : '';
613 } else if (jsonData.name !== undefined) {613 } else if (jsonData.name !== undefined) {
614 console.log('Importing from v1 json');614 console.info('Importing from v1 json');
615 jsonData.name = sanitize(jsonData.name);615 jsonData.name = sanitize(jsonData.name);
616 if (jsonData.creator_notes) {616 if (jsonData.creator_notes) {
617 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');617 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -637,7 +637,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
637 const result = await writeCharacterData(defaultAvatarPath, charJSON, pngName, request);637 const result = await writeCharacterData(defaultAvatarPath, charJSON, pngName, request);
638 return result ? pngName : '';638 return result ? pngName : '';
639 } else if (jsonData.char_name !== undefined) {//json Pygmalion notepad639 } else if (jsonData.char_name !== undefined) {//json Pygmalion notepad
640 console.log('Importing from gradio json');640 console.info('Importing from gradio json');
641 jsonData.char_name = sanitize(jsonData.char_name);641 jsonData.char_name = sanitize(jsonData.char_name);
642 if (jsonData.creator_notes) {642 if (jsonData.creator_notes) {
643 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');643 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -684,7 +684,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
684 const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories);684 const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories);
685685
686 if (jsonData.spec !== undefined) {686 if (jsonData.spec !== undefined) {
687 console.log(`Found a ${jsonData.spec} character file.`);687 console.info(`Found a ${jsonData.spec} character file.`);
688 importRisuSprites(request.user.directories, jsonData);688 importRisuSprites(request.user.directories, jsonData);
689 unsetFavFlag(jsonData);689 unsetFavFlag(jsonData);
690 jsonData = readFromV2(jsonData);690 jsonData = readFromV2(jsonData);
@@ -694,7 +694,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
694 fs.unlinkSync(uploadPath);694 fs.unlinkSync(uploadPath);
695 return result ? pngName : '';695 return result ? pngName : '';
696 } else if (jsonData.name !== undefined) {696 } else if (jsonData.name !== undefined) {
697 console.log('Found a v1 character file.');697 console.info('Found a v1 character file.');
698698
699 if (jsonData.creator_notes) {699 if (jsonData.creator_notes) {
700 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');700 jsonData.creator_notes = jsonData.creator_notes.replace('Creator\'s notes go here.', '');
@@ -853,7 +853,7 @@ router.post('/edit', urlencodedParser, async function (request, response) {
853 * @returns {void}853 * @returns {void}
854 */854 */
855router.post('/edit-attribute', jsonParser, async function (request, response) {855router.post('/edit-attribute', jsonParser, async function (request, response) {
856 console.log(request.body);856 console.debug(request.body);
857 if (!request.body) {857 if (!request.body) {
858 console.error('Error: no response body detected');858 console.error('Error: no response body detected');
859 return response.status(400).send('Error: no response body detected');859 return response.status(400).send('Error: no response body detected');
@@ -921,7 +921,7 @@ router.post('/merge-attributes', jsonParser, async function (request, response)
921 await writeCharacterData(avatarPath, JSON.stringify(character), targetImg, request);921 await writeCharacterData(avatarPath, JSON.stringify(character), targetImg, request);
922 response.sendStatus(200);922 response.sendStatus(200);
923 } else {923 } else {
924 console.log(validator.lastValidationError);924 console.error(validator.lastValidationError);
925 response.status(400).send({ message: `Validation failed for ${character.name}`, error: validator.lastValidationError });925 response.status(400).send({ message: `Validation failed for ${character.name}`, error: validator.lastValidationError });
926 }926 }
927 } catch (exception) {927 } catch (exception) {
@@ -1043,7 +1043,7 @@ router.post('/chats', jsonParser, async function (request, response) {
1043 const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;1043 const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;
10441044
1045 if (stats.size === 0) {1045 if (stats.size === 0) {
1046 console.log(`Found an empty chat file: ${pathToFile}`);1046 console.warn(`Found an empty chat file: ${pathToFile}`);
1047 res({});1047 res({});
1048 return;1048 return;
1049 }1049 }
@@ -1075,7 +1075,7 @@ router.post('/chats', jsonParser, async function (request, response) {
10751075
1076 res(chatData);1076 res(chatData);
1077 } else {1077 } else {
1078 console.log('Found an invalid or corrupted chat file:', pathToFile);1078 console.warn('Found an invalid or corrupted chat file:', pathToFile);
1079 res({});1079 res({});
1080 }1080 }
1081 }1081 }
@@ -1088,7 +1088,7 @@ router.post('/chats', jsonParser, async function (request, response) {
10881088
1089 return response.send(validFiles);1089 return response.send(validFiles);
1090 } catch (error) {1090 } catch (error) {
1091 console.log(error);1091 console.error(error);
1092 return response.send({ error: true });1092 return response.send({ error: true });
1093 }1093 }
1094});1094});
@@ -1155,7 +1155,7 @@ router.post('/import', urlencodedParser, async function (request, response) {
11551155
1156 response.send({ file_name: fileName });1156 response.send({ file_name: fileName });
1157 } catch (err) {1157 } catch (err) {
1158 console.log(err);1158 console.error(err);
1159 response.send({ error: true });1159 response.send({ error: true });
1160 }1160 }
1161});1161});
@@ -1163,14 +1163,13 @@ router.post('/import', urlencodedParser, async function (request, response) {
1163router.post('/duplicate', jsonParser, async function (request, response) {1163router.post('/duplicate', jsonParser, async function (request, response) {
1164 try {1164 try {
1165 if (!request.body.avatar_url) {1165 if (!request.body.avatar_url) {
1166 console.log('avatar URL not found in request body');1166 console.error('avatar URL not found in request body');
1167 console.log(request.body);1167 console.debug(request.body);
1168 return response.sendStatus(400);1168 return response.sendStatus(400);
1169 }1169 }
1170 let filename = path.join(request.user.directories.characters, sanitize(request.body.avatar_url));1170 let filename = path.join(request.user.directories.characters, sanitize(request.body.avatar_url));
1171 if (!fs.existsSync(filename)) {1171 if (!fs.existsSync(filename)) {
1172 console.log('file for dupe not found');1172 console.error('file for dupe not found', filename);
1173 console.log(filename);
1174 return response.sendStatus(404);1173 return response.sendStatus(404);
1175 }1174 }
1176 let suffix = 1;1175 let suffix = 1;
@@ -1198,7 +1197,7 @@ router.post('/duplicate', jsonParser, async function (request, response) {
1198 }1197 }
11991198
1200 fs.copyFileSync(filename, newFilename);1199 fs.copyFileSync(filename, newFilename);
1201 console.log(`${filename} was copied to ${newFilename}`);1200 console.info(`${filename} was copied to ${newFilename}`);
1202 response.send({ path: path.parse(newFilename).base });1201 response.send({ path: path.parse(newFilename).base });
1203 }1202 }
1204 catch (error) {1203 catch (error) {
src/endpoints/chats.js+17 -18
@@ -49,7 +49,7 @@ function backupChat(directory, name, chat) {
4949
50 removeOldBackups(directory, 'chat_', maxTotalChatBackups);50 removeOldBackups(directory, 'chat_', maxTotalChatBackups);
51 } catch (err) {51 } catch (err) {
52 console.log(`Could not backup chat for ${name}`, err);52 console.error(`Could not backup chat for ${name}`, err);
53 }53 }
54}54}
5555
@@ -277,8 +277,8 @@ router.post('/save', jsonParser, function (request, response) {
277 getBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData);277 getBackupFunction(request.user.profile.handle)(request.user.directories.backups, directoryName, jsonlData);
278 return response.send({ result: 'ok' });278 return response.send({ result: 'ok' });
279 } catch (error) {279 } catch (error) {
280 response.send(error);280 console.error(error);
281 return console.log(error);281 return response.send(error);
282 }282 }
283});283});
284284
@@ -330,17 +330,17 @@ router.post('/rename', jsonParser, async function (request, response) {
330 const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));330 const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));
331 const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));331 const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));
332 const sanitizedFileName = path.parse(pathToRenamedFile).name;332 const sanitizedFileName = path.parse(pathToRenamedFile).name;
333 console.log('Old chat name', pathToOriginalFile);333 console.info('Old chat name', pathToOriginalFile);
334 console.log('New chat name', pathToRenamedFile);334 console.info('New chat name', pathToRenamedFile);
335335
336 if (!fs.existsSync(pathToOriginalFile) || fs.existsSync(pathToRenamedFile)) {336 if (!fs.existsSync(pathToOriginalFile) || fs.existsSync(pathToRenamedFile)) {
337 console.log('Either Source or Destination files are not available');337 console.error('Either Source or Destination files are not available');
338 return response.status(400).send({ error: true });338 return response.status(400).send({ error: true });
339 }339 }
340340
341 fs.copyFileSync(pathToOriginalFile, pathToRenamedFile);341 fs.copyFileSync(pathToOriginalFile, pathToRenamedFile);
342 fs.rmSync(pathToOriginalFile);342 fs.rmSync(pathToOriginalFile);
343 console.log('Successfully renamed.');343 console.info('Successfully renamed.');
344 return response.send({ ok: true, sanitizedFileName });344 return response.send({ ok: true, sanitizedFileName });
345});345});
346346
@@ -351,12 +351,12 @@ router.post('/delete', jsonParser, function (request, response) {
351 const chatFileExists = fs.existsSync(filePath);351 const chatFileExists = fs.existsSync(filePath);
352352
353 if (!chatFileExists) {353 if (!chatFileExists) {
354 console.log(`Chat file not found '${filePath}'`);354 console.error(`Chat file not found '${filePath}'`);
355 return response.sendStatus(400);355 return response.sendStatus(400);
356 }356 }
357357
358 fs.rmSync(filePath);358 fs.rmSync(filePath);
359 console.log('Deleted chat file: ' + filePath);359 console.info(`Deleted chat file: ${filePath}`);
360 return response.send('ok');360 return response.send('ok');
361});361});
362362
@@ -373,7 +373,7 @@ router.post('/export', jsonParser, async function (request, response) {
373 const errorMessage = {373 const errorMessage = {
374 message: `Could not find JSONL file to export. Source chat file: ${filename}.`,374 message: `Could not find JSONL file to export. Source chat file: ${filename}.`,
375 };375 };
376 console.log(errorMessage.message);376 console.error(errorMessage.message);
377 return response.status(404).json(errorMessage);377 return response.status(404).json(errorMessage);
378 }378 }
379 try {379 try {
@@ -386,14 +386,14 @@ router.post('/export', jsonParser, async function (request, response) {
386 result: rawFile,386 result: rawFile,
387 };387 };
388388
389 console.log(`Chat exported as ${exportfilename}`);389 console.info(`Chat exported as ${exportfilename}`);
390 return response.status(200).json(successMessage);390 return response.status(200).json(successMessage);
391 } catch (err) {391 } catch (err) {
392 console.error(err);392 console.error(err);
393 const errorMessage = {393 const errorMessage = {
394 message: `Could not read JSONL file to export. Source chat file: ${filename}.`,394 message: `Could not read JSONL file to export. Source chat file: ${filename}.`,
395 };395 };
396 console.log(errorMessage.message);396 console.error(errorMessage.message);
397 return response.status(500).json(errorMessage);397 return response.status(500).json(errorMessage);
398 }398 }
399 }399 }
@@ -420,12 +420,11 @@ router.post('/export', jsonParser, async function (request, response) {
420 message: `Chat saved to ${exportfilename}`,420 message: `Chat saved to ${exportfilename}`,
421 result: buffer,421 result: buffer,
422 };422 };
423 console.log(`Chat exported as ${exportfilename}`);423 console.info(`Chat exported as ${exportfilename}`);
424 return response.status(200).json(successMessage);424 return response.status(200).json(successMessage);
425 });425 });
426 } catch (err) {426 } catch (err) {
427 console.log('chat export failed.');427 console.error('chat export failed.', err);
428 console.log(err);
429 return response.sendStatus(400);428 return response.sendStatus(400);
430 }429 }
431});430});
@@ -482,7 +481,7 @@ router.post('/import', urlencodedParser, function (request, response) {
482 } else if (Array.isArray(jsonData.messages)) { // Agnai's format481 } else if (Array.isArray(jsonData.messages)) { // Agnai's format
483 importFunc = importAgnaiChat;482 importFunc = importAgnaiChat;
484 } else { // Unknown format483 } else { // Unknown format
485 console.log('Incorrect chat format .json');484 console.error('Incorrect chat format .json');
486 return response.send({ error: true });485 return response.send({ error: true });
487 }486 }
488487
@@ -510,7 +509,7 @@ router.post('/import', urlencodedParser, function (request, response) {
510 const jsonData = JSON.parse(header);509 const jsonData = JSON.parse(header);
511510
512 if (!(jsonData.user_name !== undefined || jsonData.name !== undefined)) {511 if (!(jsonData.user_name !== undefined || jsonData.name !== undefined)) {
513 console.log('Incorrect chat format .jsonl');512 console.error('Incorrect chat format .jsonl');
514 return response.send({ error: true });513 return response.send({ error: true });
515 }514 }
516515
@@ -616,7 +615,7 @@ router.post('/search', jsonParser, function (request, response) {
616 break;615 break;
617 }616 }
618 } catch (error) {617 } catch (error) {
619 console.error(groupFile, 'group file is corrupted:', error);618 console.warn(groupFile, 'group file is corrupted:', error);
620 }619 }
621 }620 }
622621
src/endpoints/classify.js+2 -2
@@ -44,9 +44,9 @@ router.post('/', jsonParser, async (req, res) => {
44 }44 }
45 }45 }
4646
47 console.log('Classify input:', text);47 console.debug('Classify input:', text);
48 const result = await getResult(text);48 const result = await getResult(text);
49 console.log('Classify output:', result);49 console.debug('Classify output:', result);
5050
51 return res.json({ classification: result });51 return res.json({ classification: result });
52 } catch (error) {52 } catch (error) {
src/endpoints/content-manager.js+26 -26
@@ -71,7 +71,7 @@ export function getDefaultPresets(directories) {
7171
72 return presets;72 return presets;
73 } catch (err) {73 } catch (err) {
74 console.log('Failed to get default presets', err);74 console.warn('Failed to get default presets', err);
75 return [];75 return [];
76 }76 }
77}77}
@@ -92,7 +92,7 @@ export function getDefaultPresetFile(filename) {
92 const fileContent = fs.readFileSync(contentPath, 'utf8');92 const fileContent = fs.readFileSync(contentPath, 'utf8');
93 return JSON.parse(fileContent);93 return JSON.parse(fileContent);
94 } catch (err) {94 } catch (err) {
95 console.log(`Failed to get default file ${filename}`, err);95 console.warn(`Failed to get default file ${filename}`, err);
96 return null;96 return null;
97 }97 }
98}98}
@@ -121,21 +121,21 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
121 }121 }
122122
123 if (!contentItem.folder) {123 if (!contentItem.folder) {
124 console.log(`Content file ${contentItem.filename} has no parent folder`);124 console.warn(`Content file ${contentItem.filename} has no parent folder`);
125 continue;125 continue;
126 }126 }
127127
128 const contentPath = path.join(contentItem.folder, contentItem.filename);128 const contentPath = path.join(contentItem.folder, contentItem.filename);
129129
130 if (!fs.existsSync(contentPath)) {130 if (!fs.existsSync(contentPath)) {
131 console.log(`Content file ${contentItem.filename} is missing`);131 console.warn(`Content file ${contentItem.filename} is missing`);
132 continue;132 continue;
133 }133 }
134134
135 const contentTarget = getTargetByType(contentItem.type, directories);135 const contentTarget = getTargetByType(contentItem.type, directories);
136136
137 if (!contentTarget) {137 if (!contentTarget) {
138 console.log(`Content file ${contentItem.filename} has unknown type ${contentItem.type}`);138 console.warn(`Content file ${contentItem.filename} has unknown type ${contentItem.type}`);
139 continue;139 continue;
140 }140 }
141141
@@ -144,12 +144,12 @@ async function seedContentForUser(contentIndex, directories, forceCategories) {
144 contentLog.push(contentItem.filename);144 contentLog.push(contentItem.filename);
145145
146 if (fs.existsSync(targetPath)) {146 if (fs.existsSync(targetPath)) {
147 console.log(`Content file ${contentItem.filename} already exists in ${contentTarget}`);147 console.warn(`Content file ${contentItem.filename} already exists in ${contentTarget}`);
148 continue;148 continue;
149 }149 }
150150
151 fs.cpSync(contentPath, targetPath, { recursive: true, force: false });151 fs.cpSync(contentPath, targetPath, { recursive: true, force: false });
152 console.log(`Content file ${contentItem.filename} copied to ${contentTarget}`);152 console.info(`Content file ${contentItem.filename} copied to ${contentTarget}`);
153 anyContentAdded = true;153 anyContentAdded = true;
154 }154 }
155155
@@ -182,12 +182,12 @@ export async function checkForNewContent(directoriesList, forceCategories = [])
182 }182 }
183183
184 if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) {184 if (anyContentAdded && !contentCheckSkip && forceCategories?.length === 0) {
185 console.log();185 console.info();
186 console.log(`${color.blue('If you don\'t want to receive content updates in the future, set')} ${color.yellow('skipContentCheck')} ${color.blue('to true in the config.yaml file.')}`);186 console.info(`${color.blue('If you don\'t want to receive content updates in the future, set')} ${color.yellow('skipContentCheck')} ${color.blue('to true in the config.yaml file.')}`);
187 console.log();187 console.info();
188 }188 }
189 } catch (err) {189 } catch (err) {
190 console.log('Content check failed', err);190 console.error('Content check failed', err);
191 }191 }
192}192}
193193
@@ -331,7 +331,7 @@ async function downloadChubLorebook(id) {
331331
332 if (!result.ok) {332 if (!result.ok) {
333 const text = await result.text();333 const text = await result.text();
334 console.log('Chub returned error', result.statusText, text);334 console.error('Chub returned error', result.statusText, text);
335 throw new Error('Failed to download lorebook');335 throw new Error('Failed to download lorebook');
336 }336 }
337337
@@ -355,7 +355,7 @@ async function downloadChubCharacter(id) {
355355
356 if (!result.ok) {356 if (!result.ok) {
357 const text = await result.text();357 const text = await result.text();
358 console.log('Chub returned error', result.statusText, text);358 console.error('Chub returned error', result.statusText, text);
359 throw new Error('Failed to download character');359 throw new Error('Failed to download character');
360 }360 }
361361
@@ -376,7 +376,7 @@ async function downloadPygmalionCharacter(id) {
376376
377 if (!result.ok) {377 if (!result.ok) {
378 const text = await result.text();378 const text = await result.text();
379 console.log('Pygsite returned error', result.status, text);379 console.error('Pygsite returned error', result.status, text);
380 throw new Error('Failed to download character');380 throw new Error('Failed to download character');
381 }381 }
382382
@@ -485,7 +485,7 @@ async function downloadJannyCharacter(uuid) {
485 }485 }
486 }486 }
487487
488 console.log('Janny returned error', result.statusText, await result.text());488 console.error('Janny returned error', result.statusText, await result.text());
489 throw new Error('Failed to download character');489 throw new Error('Failed to download character');
490}490}
491491
@@ -577,7 +577,7 @@ async function downloadRisuCharacter(uuid) {
577577
578 if (!result.ok) {578 if (!result.ok) {
579 const text = await result.text();579 const text = await result.text();
580 console.log('RisuAI returned error', result.statusText, text);580 console.error('RisuAI returned error', result.statusText, text);
581 throw new Error('Failed to download character');581 throw new Error('Failed to download character');
582 }582 }
583583
@@ -673,11 +673,11 @@ router.post('/importURL', jsonParser, async (request, response) => {
673 type = chubParsed?.type;673 type = chubParsed?.type;
674674
675 if (chubParsed?.type === 'character') {675 if (chubParsed?.type === 'character') {
676 console.log('Downloading chub character:', chubParsed.id);676 console.info('Downloading chub character:', chubParsed.id);
677 result = await downloadChubCharacter(chubParsed.id);677 result = await downloadChubCharacter(chubParsed.id);
678 }678 }
679 else if (chubParsed?.type === 'lorebook') {679 else if (chubParsed?.type === 'lorebook') {
680 console.log('Downloading chub lorebook:', chubParsed.id);680 console.info('Downloading chub lorebook:', chubParsed.id);
681 result = await downloadChubLorebook(chubParsed.id);681 result = await downloadChubLorebook(chubParsed.id);
682 }682 }
683 else {683 else {
@@ -692,7 +692,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
692 type = 'character';692 type = 'character';
693 result = await downloadRisuCharacter(uuid);693 result = await downloadRisuCharacter(uuid);
694 } else if (isGeneric) {694 } else if (isGeneric) {
695 console.log('Downloading from generic url.');695 console.info('Downloading from generic url.');
696 type = 'character';696 type = 'character';
697 result = await downloadGenericPng(url);697 result = await downloadGenericPng(url);
698 } else {698 } else {
@@ -708,7 +708,7 @@ router.post('/importURL', jsonParser, async (request, response) => {
708 response.set('X-Custom-Content-Type', type);708 response.set('X-Custom-Content-Type', type);
709 return response.send(result.buffer);709 return response.send(result.buffer);
710 } catch (error) {710 } catch (error) {
711 console.log('Importing custom content failed', error);711 console.error('Importing custom content failed', error);
712 return response.sendStatus(500);712 return response.sendStatus(500);
713 }713 }
714});714});
@@ -728,22 +728,22 @@ router.post('/importUUID', jsonParser, async (request, response) => {
728 const uuidType = uuid.includes('lorebook') ? 'lorebook' : 'character';728 const uuidType = uuid.includes('lorebook') ? 'lorebook' : 'character';
729729
730 if (isPygmalion) {730 if (isPygmalion) {
731 console.log('Downloading Pygmalion character:', uuid);731 console.info('Downloading Pygmalion character:', uuid);
732 result = await downloadPygmalionCharacter(uuid);732 result = await downloadPygmalionCharacter(uuid);
733 } else if (isJannny) {733 } else if (isJannny) {
734 console.log('Downloading Janitor character:', uuid.split('_')[0]);734 console.info('Downloading Janitor character:', uuid.split('_')[0]);
735 result = await downloadJannyCharacter(uuid.split('_')[0]);735 result = await downloadJannyCharacter(uuid.split('_')[0]);
736 } else if (isAICC) {736 } else if (isAICC) {
737 const [, author, card] = uuid.split('/');737 const [, author, card] = uuid.split('/');
738 console.log('Downloading AICC character:', `${author}/${card}`);738 console.info('Downloading AICC character:', `${author}/${card}`);
739 result = await downloadAICCCharacter(`${author}/${card}`);739 result = await downloadAICCCharacter(`${author}/${card}`);
740 } else {740 } else {
741 if (uuidType === 'character') {741 if (uuidType === 'character') {
742 console.log('Downloading chub character:', uuid);742 console.info('Downloading chub character:', uuid);
743 result = await downloadChubCharacter(uuid);743 result = await downloadChubCharacter(uuid);
744 }744 }
745 else if (uuidType === 'lorebook') {745 else if (uuidType === 'lorebook') {
746 console.log('Downloading chub lorebook:', uuid);746 console.info('Downloading chub lorebook:', uuid);
747 result = await downloadChubLorebook(uuid);747 result = await downloadChubLorebook(uuid);
748 }748 }
749 else {749 else {
@@ -756,7 +756,7 @@ router.post('/importUUID', jsonParser, async (request, response) => {
756 response.set('X-Custom-Content-Type', uuidType);756 response.set('X-Custom-Content-Type', uuidType);
757 return response.send(result.buffer);757 return response.send(result.buffer);
758 } catch (error) {758 } catch (error) {
759 console.log('Importing custom content failed', error);759 console.error('Importing custom content failed', error);
760 return response.sendStatus(500);760 return response.sendStatus(500);
761 }761 }
762});762});
src/endpoints/extensions.js+16 -16
@@ -80,7 +80,7 @@ router.post('/install', jsonParser, async (request, response) => {
80 const { url, global } = request.body;80 const { url, global } = request.body;
8181
82 if (global && !request.user.profile.admin) {82 if (global && !request.user.profile.admin) {
83 console.warn(`User ${request.user.profile.handle} does not have permission to install global extensions.`);83 console.error(`User ${request.user.profile.handle} does not have permission to install global extensions.`);
84 return response.status(403).send('Forbidden: No permission to install global extensions.');84 return response.status(403).send('Forbidden: No permission to install global extensions.');
85 }85 }
8686
@@ -92,13 +92,13 @@ router.post('/install', jsonParser, async (request, response) => {
92 }92 }
9393
94 await git.clone(url, extensionPath, { '--depth': 1 });94 await git.clone(url, extensionPath, { '--depth': 1 });
95 console.log(`Extension has been cloned at ${extensionPath}`);95 console.info(`Extension has been cloned at ${extensionPath}`);
9696
97 const { version, author, display_name } = await getManifest(extensionPath);97 const { version, author, display_name } = await getManifest(extensionPath);
9898
99 return response.send({ version, author, display_name, extensionPath });99 return response.send({ version, author, display_name, extensionPath });
100 } catch (error) {100 } catch (error) {
101 console.log('Importing custom content failed', error);101 console.error('Importing custom content failed', error);
102 return response.status(500).send(`Server Error: ${error.message}`);102 return response.status(500).send(`Server Error: ${error.message}`);
103 }103 }
104});104});
@@ -124,7 +124,7 @@ router.post('/update', jsonParser, async (request, response) => {
124 const { extensionName, global } = request.body;124 const { extensionName, global } = request.body;
125125
126 if (global && !request.user.profile.admin) {126 if (global && !request.user.profile.admin) {
127 console.warn(`User ${request.user.profile.handle} does not have permission to update global extensions.`);127 console.error(`User ${request.user.profile.handle} does not have permission to update global extensions.`);
128 return response.status(403).send('Forbidden: No permission to update global extensions.');128 return response.status(403).send('Forbidden: No permission to update global extensions.');
129 }129 }
130130
@@ -139,9 +139,9 @@ router.post('/update', jsonParser, async (request, response) => {
139 const currentBranch = await git.cwd(extensionPath).branch();139 const currentBranch = await git.cwd(extensionPath).branch();
140 if (!isUpToDate) {140 if (!isUpToDate) {
141 await git.cwd(extensionPath).pull('origin', currentBranch.current);141 await git.cwd(extensionPath).pull('origin', currentBranch.current);
142 console.log(`Extension has been updated at ${extensionPath}`);142 console.info(`Extension has been updated at ${extensionPath}`);
143 } else {143 } else {
144 console.log(`Extension is up to date at ${extensionPath}`);144 console.info(`Extension is up to date at ${extensionPath}`);
145 }145 }
146 await git.cwd(extensionPath).fetch('origin');146 await git.cwd(extensionPath).fetch('origin');
147 const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);147 const fullCommitHash = await git.cwd(extensionPath).revparse(['HEAD']);
@@ -150,7 +150,7 @@ router.post('/update', jsonParser, async (request, response) => {
150 return response.send({ shortCommitHash, extensionPath, isUpToDate, remoteUrl });150 return response.send({ shortCommitHash, extensionPath, isUpToDate, remoteUrl });
151151
152 } catch (error) {152 } catch (error) {
153 console.log('Updating custom content failed', error);153 console.error('Updating custom content failed', error);
154 return response.status(500).send(`Server Error: ${error.message}`);154 return response.status(500).send(`Server Error: ${error.message}`);
155 }155 }
156});156});
@@ -164,7 +164,7 @@ router.post('/move', jsonParser, async (request, response) => {
164 }164 }
165165
166 if (!request.user.profile.admin) {166 if (!request.user.profile.admin) {
167 console.warn(`User ${request.user.profile.handle} does not have permission to move extensions.`);167 console.error(`User ${request.user.profile.handle} does not have permission to move extensions.`);
168 return response.status(403).send('Forbidden: No permission to move extensions.');168 return response.status(403).send('Forbidden: No permission to move extensions.');
169 }169 }
170170
@@ -190,11 +190,11 @@ router.post('/move', jsonParser, async (request, response) => {
190190
191 fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });191 fs.cpSync(sourcePath, destinationPath, { recursive: true, force: true });
192 fs.rmSync(sourcePath, { recursive: true, force: true });192 fs.rmSync(sourcePath, { recursive: true, force: true });
193 console.log(`Extension has been moved from ${sourcePath} to ${destinationPath}`);193 console.info(`Extension has been moved from ${sourcePath} to ${destinationPath}`);
194194
195 return response.sendStatus(204);195 return response.sendStatus(204);
196 } catch (error) {196 } catch (error) {
197 console.log('Moving extension failed', error);197 console.error('Moving extension failed', error);
198 return response.status(500).send('Internal Server Error. Try again later.');198 return response.status(500).send('Internal Server Error. Try again later.');
199 }199 }
200});200});
@@ -237,13 +237,13 @@ router.post('/version', jsonParser, async (request, response) => {
237 // get only the working branch237 // get only the working branch
238 const currentBranchName = currentBranch.current;238 const currentBranchName = currentBranch.current;
239 await git.cwd(extensionPath).fetch('origin');239 await git.cwd(extensionPath).fetch('origin');
240 console.log(extensionName, currentBranchName, currentCommitHash);240 console.debug(extensionName, currentBranchName, currentCommitHash);
241 const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);241 const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);
242242
243 return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });243 return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });
244244
245 } catch (error) {245 } catch (error) {
246 console.log('Getting extension version failed', error);246 console.error('Getting extension version failed', error);
247 return response.status(500).send(`Server Error: ${error.message}`);247 return response.status(500).send(`Server Error: ${error.message}`);
248 }248 }
249});249});
@@ -265,7 +265,7 @@ router.post('/delete', jsonParser, async (request, response) => {
265 const { extensionName, global } = request.body;265 const { extensionName, global } = request.body;
266266
267 if (global && !request.user.profile.admin) {267 if (global && !request.user.profile.admin) {
268 console.warn(`User ${request.user.profile.handle} does not have permission to delete global extensions.`);268 console.error(`User ${request.user.profile.handle} does not have permission to delete global extensions.`);
269 return response.status(403).send('Forbidden: No permission to delete global extensions.');269 return response.status(403).send('Forbidden: No permission to delete global extensions.');
270 }270 }
271271
@@ -277,12 +277,12 @@ router.post('/delete', jsonParser, async (request, response) => {
277 }277 }
278278
279 await fs.promises.rm(extensionPath, { recursive: true });279 await fs.promises.rm(extensionPath, { recursive: true });
280 console.log(`Extension has been deleted at ${extensionPath}`);280 console.info(`Extension has been deleted at ${extensionPath}`);
281281
282 return response.send(`Extension has been deleted at ${extensionPath}`);282 return response.send(`Extension has been deleted at ${extensionPath}`);
283283
284 } catch (error) {284 } catch (error) {
285 console.log('Deleting custom content failed', error);285 console.error('Deleting custom content failed', error);
286 return response.status(500).send(`Server Error: ${error.message}`);286 return response.status(500).send(`Server Error: ${error.message}`);
287 }287 }
288});288});
@@ -323,7 +323,7 @@ router.get('/discover', jsonParser, function (request, response) {
323323
324 // Combine all extensions324 // Combine all extensions
325 const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];325 const allExtensions = [...builtInExtensions, ...userExtensions, ...globalExtensions];
326 console.log('Extensions available for', request.user.profile.handle, allExtensions);326 console.info('Extensions available for', request.user.profile.handle, allExtensions);
327327
328 return response.send(allExtensions);328 return response.send(allExtensions);
329});329});
src/endpoints/files.js+7 -7
@@ -21,7 +21,7 @@ router.post('/sanitize-filename', jsonParser, async (request, response) => {
21 const sanitizedFilename = sanitize(fileName);21 const sanitizedFilename = sanitize(fileName);
22 return response.send({ fileName: sanitizedFilename });22 return response.send({ fileName: sanitizedFilename });
23 } catch (error) {23 } catch (error) {
24 console.log(error);24 console.error(error);
25 return response.sendStatus(500);25 return response.sendStatus(500);
26 }26 }
27});27});
@@ -44,10 +44,10 @@ router.post('/upload', jsonParser, async (request, response) => {
44 const pathToUpload = path.join(request.user.directories.files, request.body.name);44 const pathToUpload = path.join(request.user.directories.files, request.body.name);
45 writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');45 writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');
46 const url = clientRelativePath(request.user.directories.root, pathToUpload);46 const url = clientRelativePath(request.user.directories.root, pathToUpload);
47 console.log(`Uploaded file: ${url} from ${request.user.profile.handle}`);47 console.info(`Uploaded file: ${url} from ${request.user.profile.handle}`);
48 return response.send({ path: url });48 return response.send({ path: url });
49 } catch (error) {49 } catch (error) {
50 console.log(error);50 console.error(error);
51 return response.sendStatus(500);51 return response.sendStatus(500);
52 }52 }
53});53});
@@ -68,10 +68,10 @@ router.post('/delete', jsonParser, async (request, response) => {
68 }68 }
6969
70 fs.rmSync(pathToDelete);70 fs.rmSync(pathToDelete);
71 console.log(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`);71 console.info(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`);
72 return response.sendStatus(200);72 return response.sendStatus(200);
73 } catch (error) {73 } catch (error) {
74 console.log(error);74 console.error(error);
75 return response.sendStatus(500);75 return response.sendStatus(500);
76 }76 }
77});77});
@@ -87,7 +87,7 @@ router.post('/verify', jsonParser, async (request, response) => {
87 for (const url of request.body.urls) {87 for (const url of request.body.urls) {
88 const pathToVerify = path.join(request.user.directories.root, url);88 const pathToVerify = path.join(request.user.directories.root, url);
89 if (!pathToVerify.startsWith(request.user.directories.files)) {89 if (!pathToVerify.startsWith(request.user.directories.files)) {
90 console.debug(`File verification: Invalid path: ${pathToVerify}`);90 console.warn(`File verification: Invalid path: ${pathToVerify}`);
91 continue;91 continue;
92 }92 }
93 const fileExists = fs.existsSync(pathToVerify);93 const fileExists = fs.existsSync(pathToVerify);
@@ -96,7 +96,7 @@ router.post('/verify', jsonParser, async (request, response) => {
9696
97 return response.send(verified);97 return response.send(verified);
98 } catch (error) {98 } catch (error) {
99 console.log(error);99 console.error(error);
100 return response.sendStatus(500);100 return response.sendStatus(500);
101 }101 }
102});102});
src/endpoints/google.js+3 -3
@@ -34,7 +34,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
34 generationConfig: { maxOutputTokens: 1000 },34 generationConfig: { maxOutputTokens: 1000 },
35 };35 };
3636
37 console.log('Multimodal captioning request', model, body);37 console.debug('Multimodal captioning request', model, body);
3838
39 const result = await fetch(url, {39 const result = await fetch(url, {
40 body: JSON.stringify(body),40 body: JSON.stringify(body),
@@ -46,13 +46,13 @@ router.post('/caption-image', jsonParser, async (request, response) => {
4646
47 if (!result.ok) {47 if (!result.ok) {
48 const error = await result.json();48 const error = await result.json();
49 console.log(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);49 console.error(`Google AI Studio API returned error: ${result.status} ${result.statusText}`, error);
50 return response.status(result.status).send({ error: true });50 return response.status(result.status).send({ error: true });
51 }51 }
5252
53 /** @type {any} */53 /** @type {any} */
54 const data = await result.json();54 const data = await result.json();
55 console.log('Multimodal captioning response', data);55 console.debug('Multimodal captioning response', data);
5656
57 const candidates = data?.candidates;57 const candidates = data?.candidates;
58 if (!candidates) {58 if (!candidates) {
src/endpoints/groups.js+1 -1
@@ -114,7 +114,7 @@ router.post('/delete', jsonParser, async (request, response) => {
114114
115 if (group && Array.isArray(group.chats)) {115 if (group && Array.isArray(group.chats)) {
116 for (const chat of group.chats) {116 for (const chat of group.chats) {
117 console.log('Deleting group chat', chat);117 console.info('Deleting group chat', chat);
118 const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`);118 const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`);
119119
120 if (fs.existsSync(pathToFile)) {120 if (fs.existsSync(pathToFile)) {
src/endpoints/horde.js+14 -14
@@ -155,7 +155,7 @@ router.post('/cancel-task', jsonParser, async (request, response) => {
155 });155 });
156156
157 const data = await fetchResult.json();157 const data = await fetchResult.json();
158 console.log(`Cancelled Horde task ${taskId}`);158 console.info(`Cancelled Horde task ${taskId}`);
159 return response.send(data);159 return response.send(data);
160 } catch (error) {160 } catch (error) {
161 console.error(error);161 console.error(error);
@@ -174,7 +174,7 @@ router.post('/task-status', jsonParser, async (request, response) => {
174 });174 });
175175
176 const data = await fetchResult.json();176 const data = await fetchResult.json();
177 console.log(`Horde task ${taskId} status:`, data);177 console.info(`Horde task ${taskId} status:`, data);
178 return response.send(data);178 return response.send(data);
179 } catch (error) {179 } catch (error) {
180 console.error(error);180 console.error(error);
@@ -187,7 +187,7 @@ router.post('/generate-text', jsonParser, async (request, response) => {
187 const url = 'https://aihorde.net/api/v2/generate/text/async';187 const url = 'https://aihorde.net/api/v2/generate/text/async';
188 const agent = await getClientAgent();188 const agent = await getClientAgent();
189189
190 console.log(request.body);190 console.debug(request.body);
191 try {191 try {
192 const result = await fetch(url, {192 const result = await fetch(url, {
193 method: 'POST',193 method: 'POST',
@@ -201,14 +201,14 @@ router.post('/generate-text', jsonParser, async (request, response) => {
201201
202 if (!result.ok) {202 if (!result.ok) {
203 const message = await result.text();203 const message = await result.text();
204 console.log('Horde returned an error:', message);204 console.error('Horde returned an error:', message);
205 return response.send({ error: { message } });205 return response.send({ error: { message } });
206 }206 }
207207
208 const data = await result.json();208 const data = await result.json();
209 return response.send(data);209 return response.send(data);
210 } catch (error) {210 } catch (error) {
211 console.log(error);211 console.error(error);
212 return response.send({ error: true });212 return response.send({ error: true });
213 }213 }
214});214});
@@ -254,7 +254,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
254 for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {254 for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
255 await delay(CHECK_INTERVAL);255 await delay(CHECK_INTERVAL);
256 const status = await ai_horde.getInterrogationStatus(result.id);256 const status = await ai_horde.getInterrogationStatus(result.id);
257 console.log(status);257 console.info(status);
258258
259 if (status.state === HordeAsyncRequestStates.done) {259 if (status.state === HordeAsyncRequestStates.done) {
260260
@@ -263,7 +263,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
263 return response.sendStatus(500);263 return response.sendStatus(500);
264 }264 }
265265
266 console.log('Image interrogation result:', status);266 console.debug('Image interrogation result:', status);
267 const caption = status?.forms[0]?.result?.caption || '';267 const caption = status?.forms[0]?.result?.caption || '';
268268
269 if (!caption) {269 if (!caption) {
@@ -275,7 +275,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
275 }275 }
276276
277 if (status.state === HordeAsyncRequestStates.faulted || status.state === HordeAsyncRequestStates.cancelled) {277 if (status.state === HordeAsyncRequestStates.faulted || status.state === HordeAsyncRequestStates.cancelled) {
278 console.log('Image interrogation request is not successful.');278 console.error('Image interrogation request is not successful.');
279 return response.sendStatus(503);279 return response.sendStatus(503);
280 }280 }
281 }281 }
@@ -315,7 +315,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
315 try {315 try {
316 const maxLength = PROMPT_THRESHOLD - String(request.body.negative_prompt).length - 5;316 const maxLength = PROMPT_THRESHOLD - String(request.body.negative_prompt).length - 5;
317 if (String(request.body.prompt).length > maxLength) {317 if (String(request.body.prompt).length > maxLength) {
318 console.log('Stable Horde prompt is too long, truncating...');318 console.warn('Stable Horde prompt is too long, truncating...');
319 request.body.prompt = String(request.body.prompt).substring(0, maxLength);319 request.body.prompt = String(request.body.prompt).substring(0, maxLength);
320 }320 }
321321
@@ -324,14 +324,14 @@ router.post('/generate-image', jsonParser, async (request, response) => {
324 const sanitized = sanitizeHordeImagePrompt(request.body.prompt);324 const sanitized = sanitizeHordeImagePrompt(request.body.prompt);
325325
326 if (request.body.prompt !== sanitized) {326 if (request.body.prompt !== sanitized) {
327 console.log('Stable Horde prompt was sanitized.');327 console.info('Stable Horde prompt was sanitized.');
328 }328 }
329329
330 request.body.prompt = sanitized;330 request.body.prompt = sanitized;
331 }331 }
332332
333 const api_key_horde = readSecret(request.user.directories, SECRET_KEYS.HORDE) || ANONYMOUS_KEY;333 const api_key_horde = readSecret(request.user.directories, SECRET_KEYS.HORDE) || ANONYMOUS_KEY;
334 console.log('Stable Horde request:', request.body);334 console.debug('Stable Horde request:', request.body);
335335
336 const ai_horde = await getHordeClient();336 const ai_horde = await getHordeClient();
337 // noinspection JSCheckFunctionSignatures -- see @ts-ignore - use_gfpgan337 // noinspection JSCheckFunctionSignatures -- see @ts-ignore - use_gfpgan
@@ -364,12 +364,12 @@ router.post('/generate-image', jsonParser, async (request, response) => {
364 return response.sendStatus(400);364 return response.sendStatus(400);
365 }365 }
366366
367 console.log('Horde image generation request:', generation);367 console.debug('Horde image generation request:', generation);
368368
369 const controller = new AbortController();369 const controller = new AbortController();
370 request.socket.removeAllListeners('close');370 request.socket.removeAllListeners('close');
371 request.socket.on('close', function () {371 request.socket.on('close', function () {
372 console.log('Horde image generation request aborted.');372 console.info('Horde image generation request aborted.');
373 controller.abort();373 controller.abort();
374 if (generation.id) ai_horde.deleteImageGenerationRequest(generation.id);374 if (generation.id) ai_horde.deleteImageGenerationRequest(generation.id);
375 });375 });
@@ -378,7 +378,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
378 controller.signal.throwIfAborted();378 controller.signal.throwIfAborted();
379 await delay(CHECK_INTERVAL);379 await delay(CHECK_INTERVAL);
380 const check = await ai_horde.getImageGenerationCheck(generation.id);380 const check = await ai_horde.getImageGenerationCheck(generation.id);
381 console.log(check);381 console.info(check);
382382
383 if (check.done) {383 if (check.done) {
384 const result = await ai_horde.getImageGenerationStatus(generation.id);384 const result = await ai_horde.getImageGenerationStatus(generation.id);
src/endpoints/images.js+1 -1
@@ -71,7 +71,7 @@ router.post('/upload', jsonParser, async (request, response) => {
71 await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer));71 await fs.promises.writeFile(pathToNewFile, new Uint8Array(imageBuffer));
72 response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) });72 response.send({ path: clientRelativePath(request.user.directories.root, pathToNewFile) });
73 } catch (error) {73 } catch (error) {
74 console.log(error);74 console.error(error);
75 response.status(500).send({ error: 'Failed to save the image' });75 response.status(500).send({ error: 'Failed to save the image' });
76 }76 }
77});77});
src/endpoints/novelai.js+16 -16
@@ -120,7 +120,7 @@ router.post('/status', jsonParser, async function (req, res) {
120 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);120 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);
121121
122 if (!api_key_novel) {122 if (!api_key_novel) {
123 console.log('NovelAI Access Token is missing.');123 console.error('NovelAI Access Token is missing.');
124 return res.sendStatus(400);124 return res.sendStatus(400);
125 }125 }
126126
@@ -137,15 +137,15 @@ router.post('/status', jsonParser, async function (req, res) {
137 const data = await response.json();137 const data = await response.json();
138 return res.send(data);138 return res.send(data);
139 } else if (response.status == 401) {139 } else if (response.status == 401) {
140 console.log('NovelAI Access Token is incorrect.');140 console.error('NovelAI Access Token is incorrect.');
141 return res.send({ error: true });141 return res.send({ error: true });
142 }142 }
143 else {143 else {
144 console.log('NovelAI returned an error:', response.statusText);144 console.error('NovelAI returned an error:', response.statusText);
145 return res.send({ error: true });145 return res.send({ error: true });
146 }146 }
147 } catch (error) {147 } catch (error) {
148 console.log(error);148 console.error(error);
149 return res.send({ error: true });149 return res.send({ error: true });
150 }150 }
151});151});
@@ -156,7 +156,7 @@ router.post('/generate', jsonParser, async function (req, res) {
156 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);156 const api_key_novel = readSecret(req.user.directories, SECRET_KEYS.NOVEL);
157157
158 if (!api_key_novel) {158 if (!api_key_novel) {
159 console.log('NovelAI Access Token is missing.');159 console.error('NovelAI Access Token is missing.');
160 return res.sendStatus(400);160 return res.sendStatus(400);
161 }161 }
162162
@@ -241,7 +241,7 @@ router.post('/generate', jsonParser, async function (req, res) {
241 }241 }
242 }242 }
243243
244 console.log(util.inspect(data, { depth: 4 }));244 console.debug(util.inspect(data, { depth: 4 }));
245245
246 const args = {246 const args = {
247 body: JSON.stringify(data),247 body: JSON.stringify(data),
@@ -261,7 +261,7 @@ router.post('/generate', jsonParser, async function (req, res) {
261 if (!response.ok) {261 if (!response.ok) {
262 const text = await response.text();262 const text = await response.text();
263 let message = text;263 let message = text;
264 console.log(`Novel API returned error: ${response.status} ${response.statusText} ${text}`);264 console.error(`Novel API returned error: ${response.status} ${response.statusText} ${text}`);
265265
266 try {266 try {
267 const data = JSON.parse(text);267 const data = JSON.parse(text);
@@ -276,7 +276,7 @@ router.post('/generate', jsonParser, async function (req, res) {
276276
277 /** @type {any} */277 /** @type {any} */
278 const data = await response.json();278 const data = await response.json();
279 console.log('NovelAI Output', data?.output);279 console.debug('NovelAI Output', data?.output);
280 return res.send(data);280 return res.send(data);
281 }281 }
282 } catch (error) {282 } catch (error) {
@@ -292,12 +292,12 @@ router.post('/generate-image', jsonParser, async (request, response) => {
292 const key = readSecret(request.user.directories, SECRET_KEYS.NOVEL);292 const key = readSecret(request.user.directories, SECRET_KEYS.NOVEL);
293293
294 if (!key) {294 if (!key) {
295 console.log('NovelAI Access Token is missing.');295 console.error('NovelAI Access Token is missing.');
296 return response.sendStatus(400);296 return response.sendStatus(400);
297 }297 }
298298
299 try {299 try {
300 console.log('NAI Diffusion request:', request.body);300 console.debug('NAI Diffusion request:', request.body);
301 const generateUrl = `${IMAGE_NOVELAI}/ai/generate-image`;301 const generateUrl = `${IMAGE_NOVELAI}/ai/generate-image`;
302 const generateResult = await fetch(generateUrl, {302 const generateResult = await fetch(generateUrl, {
303 method: 'POST',303 method: 'POST',
@@ -358,7 +358,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
358358
359 if (!generateResult.ok) {359 if (!generateResult.ok) {
360 const text = await generateResult.text();360 const text = await generateResult.text();
361 console.log('NovelAI returned an error.', generateResult.statusText, text);361 console.error('NovelAI returned an error.', generateResult.statusText, text);
362 return response.sendStatus(500);362 return response.sendStatus(500);
363 }363 }
364364
@@ -366,7 +366,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
366 const imageBuffer = await extractFileFromZipBuffer(archiveBuffer, '.png');366 const imageBuffer = await extractFileFromZipBuffer(archiveBuffer, '.png');
367367
368 if (!imageBuffer) {368 if (!imageBuffer) {
369 console.warn('NovelAI generated an image, but the PNG file was not found.');369 console.error('NovelAI generated an image, but the PNG file was not found.');
370 return response.sendStatus(500);370 return response.sendStatus(500);
371 }371 }
372372
@@ -378,7 +378,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
378 }378 }
379379
380 try {380 try {
381 console.debug('Upscaling image...');381 console.info('Upscaling image...');
382 const upscaleUrl = `${API_NOVELAI}/ai/upscale`;382 const upscaleUrl = `${API_NOVELAI}/ai/upscale`;
383 const upscaleResult = await fetch(upscaleUrl, {383 const upscaleResult = await fetch(upscaleUrl, {
384 method: 'POST',384 method: 'POST',
@@ -413,7 +413,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
413 return response.send(originalBase64);413 return response.send(originalBase64);
414 }414 }
415 } catch (error) {415 } catch (error) {
416 console.log(error);416 console.error(error);
417 return response.sendStatus(500);417 return response.sendStatus(500);
418 }418 }
419});419});
@@ -422,7 +422,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
422 const token = readSecret(request.user.directories, SECRET_KEYS.NOVEL);422 const token = readSecret(request.user.directories, SECRET_KEYS.NOVEL);
423423
424 if (!token) {424 if (!token) {
425 console.log('NovelAI Access Token is missing.');425 console.error('NovelAI Access Token is missing.');
426 return response.sendStatus(400);426 return response.sendStatus(400);
427 }427 }
428428
@@ -445,7 +445,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
445445
446 if (!result.ok) {446 if (!result.ok) {
447 const errorText = await result.text();447 const errorText = await result.text();
448 console.log('NovelAI returned an error.', result.statusText, errorText);448 console.error('NovelAI returned an error.', result.statusText, errorText);
449 return response.sendStatus(500);449 return response.sendStatus(500);
450 }450 }
451451
src/endpoints/openai.js+16 -16
@@ -63,7 +63,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
63 }63 }
6464
65 if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {65 if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {
66 console.log('No key found for API', request.body.api);66 console.error('No key found for API', request.body.api);
67 return response.sendStatus(400);67 return response.sendStatus(400);
68 }68 }
6969
@@ -93,7 +93,7 @@ router.post('/caption-image', jsonParser, async (request, response) => {
93 excludeKeysByYaml(body, request.body.custom_exclude_body);93 excludeKeysByYaml(body, request.body.custom_exclude_body);
94 }94 }
9595
96 console.log('Multimodal captioning request', body);96 console.error('Multimodal captioning request', body);
9797
98 let apiUrl = '';98 let apiUrl = '';
9999
@@ -158,13 +158,13 @@ router.post('/caption-image', jsonParser, async (request, response) => {
158158
159 if (!result.ok) {159 if (!result.ok) {
160 const text = await result.text();160 const text = await result.text();
161 console.log('Multimodal captioning request failed', result.statusText, text);161 console.error('Multimodal captioning request failed', result.statusText, text);
162 return response.status(500).send(text);162 return response.status(500).send(text);
163 }163 }
164164
165 /** @type {any} */165 /** @type {any} */
166 const data = await result.json();166 const data = await result.json();
167 console.log('Multimodal captioning response', data);167 console.debug('Multimodal captioning response', data);
168 const caption = data?.choices[0]?.message?.content;168 const caption = data?.choices[0]?.message?.content;
169169
170 if (!caption) {170 if (!caption) {
@@ -184,17 +184,17 @@ router.post('/transcribe-audio', urlencodedParser, async (request, response) =>
184 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);184 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
185185
186 if (!key) {186 if (!key) {
187 console.log('No OpenAI key found');187 console.error('No OpenAI key found');
188 return response.sendStatus(400);188 return response.sendStatus(400);
189 }189 }
190190
191 if (!request.file) {191 if (!request.file) {
192 console.log('No audio file found');192 console.error('No audio file found');
193 return response.sendStatus(400);193 return response.sendStatus(400);
194 }194 }
195195
196 const formData = new FormData();196 const formData = new FormData();
197 console.log('Processing audio file', request.file.path);197 console.info('Processing audio file', request.file.path);
198 formData.append('file', fs.createReadStream(request.file.path), { filename: 'audio.wav', contentType: 'audio/wav' });198 formData.append('file', fs.createReadStream(request.file.path), { filename: 'audio.wav', contentType: 'audio/wav' });
199 formData.append('model', request.body.model);199 formData.append('model', request.body.model);
200200
@@ -213,13 +213,13 @@ router.post('/transcribe-audio', urlencodedParser, async (request, response) =>
213213
214 if (!result.ok) {214 if (!result.ok) {
215 const text = await result.text();215 const text = await result.text();
216 console.log('OpenAI request failed', result.statusText, text);216 console.error('OpenAI request failed', result.statusText, text);
217 return response.status(500).send(text);217 return response.status(500).send(text);
218 }218 }
219219
220 fs.rmSync(request.file.path);220 fs.rmSync(request.file.path);
221 const data = await result.json();221 const data = await result.json();
222 console.log('OpenAI transcription response', data);222 console.debug('OpenAI transcription response', data);
223 return response.json(data);223 return response.json(data);
224 } catch (error) {224 } catch (error) {
225 console.error('OpenAI transcription failed', error);225 console.error('OpenAI transcription failed', error);
@@ -232,7 +232,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
232 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);232 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
233233
234 if (!key) {234 if (!key) {
235 console.log('No OpenAI key found');235 console.error('No OpenAI key found');
236 return response.sendStatus(400);236 return response.sendStatus(400);
237 }237 }
238238
@@ -253,7 +253,7 @@ router.post('/generate-voice', jsonParser, async (request, response) => {
253253
254 if (!result.ok) {254 if (!result.ok) {
255 const text = await result.text();255 const text = await result.text();
256 console.log('OpenAI request failed', result.statusText, text);256 console.error('OpenAI request failed', result.statusText, text);
257 return response.status(500).send(text);257 return response.status(500).send(text);
258 }258 }
259259
@@ -271,11 +271,11 @@ router.post('/generate-image', jsonParser, async (request, response) => {
271 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);271 const key = readSecret(request.user.directories, SECRET_KEYS.OPENAI);
272272
273 if (!key) {273 if (!key) {
274 console.log('No OpenAI key found');274 console.error('No OpenAI key found');
275 return response.sendStatus(400);275 return response.sendStatus(400);
276 }276 }
277277
278 console.log('OpenAI request', request.body);278 console.debug('OpenAI request', request.body);
279279
280 const result = await fetch('https://api.openai.com/v1/images/generations', {280 const result = await fetch('https://api.openai.com/v1/images/generations', {
281 method: 'POST',281 method: 'POST',
@@ -288,7 +288,7 @@ router.post('/generate-image', jsonParser, async (request, response) => {
288288
289 if (!result.ok) {289 if (!result.ok) {
290 const text = await result.text();290 const text = await result.text();
291 console.log('OpenAI request failed', result.statusText, text);291 console.error('OpenAI request failed', result.statusText, text);
292 return response.status(500).send(text);292 return response.status(500).send(text);
293 }293 }
294294
@@ -308,7 +308,7 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
308 const { input, provider_endpoint, response_format, voice, speed, model } = request.body;308 const { input, provider_endpoint, response_format, voice, speed, model } = request.body;
309309
310 if (!provider_endpoint) {310 if (!provider_endpoint) {
311 console.log('No OpenAI-compatible TTS provider endpoint provided');311 console.error('No OpenAI-compatible TTS provider endpoint provided');
312 return response.sendStatus(400);312 return response.sendStatus(400);
313 }313 }
314314
@@ -329,7 +329,7 @@ custom.post('/generate-voice', jsonParser, async (request, response) => {
329329
330 if (!result.ok) {330 if (!result.ok) {
331 const text = await result.text();331 const text = await result.text();
332 console.log('OpenAI request failed', result.statusText, text);332 console.error('OpenAI request failed', result.statusText, text);
333 return response.status(500).send(text);333 return response.status(500).send(text);
334 }334 }
335335
src/endpoints/presets.js+1 -1
@@ -96,7 +96,7 @@ router.post('/restore', jsonParser, function (request, response) {
9696
97 return response.send(result);97 return response.send(result);
98 } catch (error) {98 } catch (error) {
99 console.log(error);99 console.error(error);
100 return response.sendStatus(500);100 return response.sendStatus(500);
101 }101 }
102});102});
src/endpoints/search.js+21 -21
@@ -94,25 +94,25 @@ router.post('/serpapi', jsonParser, async (request, response) => {
94 const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI);94 const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI);
9595
96 if (!key) {96 if (!key) {
97 console.log('No SerpApi key found');97 console.error('No SerpApi key found');
98 return response.sendStatus(400);98 return response.sendStatus(400);
99 }99 }
100100
101 const { query } = request.body;101 const { query } = request.body;
102 const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`);102 const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`);
103103
104 console.log('SerpApi query', query);104 console.debug('SerpApi query', query);
105105
106 if (!result.ok) {106 if (!result.ok) {
107 const text = await result.text();107 const text = await result.text();
108 console.log('SerpApi request failed', result.statusText, text);108 console.error('SerpApi request failed', result.statusText, text);
109 return response.status(500).send(text);109 return response.status(500).send(text);
110 }110 }
111111
112 const data = await result.json();112 const data = await result.json();
113 return response.json(data);113 return response.json(data);
114 } catch (error) {114 } catch (error) {
115 console.log(error);115 console.error(error);
116 return response.sendStatus(500);116 return response.sendStatus(500);
117 }117 }
118});118});
@@ -128,7 +128,7 @@ router.post('/transcript', jsonParser, async (request, response) => {
128 const json = request.body.json;128 const json = request.body.json;
129129
130 if (!id) {130 if (!id) {
131 console.log('Id is required for /transcript');131 console.error('Id is required for /transcript');
132 return response.sendStatus(400);132 return response.sendStatus(400);
133 }133 }
134134
@@ -153,7 +153,7 @@ router.post('/transcript', jsonParser, async (request, response) => {
153 throw error;153 throw error;
154 }154 }
155 } catch (error) {155 } catch (error) {
156 console.log(error);156 console.error(error);
157 return response.sendStatus(500);157 return response.sendStatus(500);
158 }158 }
159});159});
@@ -163,17 +163,17 @@ router.post('/searxng', jsonParser, async (request, response) => {
163 const { baseUrl, query, preferences } = request.body;163 const { baseUrl, query, preferences } = request.body;
164164
165 if (!baseUrl || !query) {165 if (!baseUrl || !query) {
166 console.log('Missing required parameters for /searxng');166 console.error('Missing required parameters for /searxng');
167 return response.sendStatus(400);167 return response.sendStatus(400);
168 }168 }
169169
170 console.log('SearXNG query', baseUrl, query);170 console.debug('SearXNG query', baseUrl, query);
171171
172 const mainPageUrl = new URL(baseUrl);172 const mainPageUrl = new URL(baseUrl);
173 const mainPageRequest = await fetch(mainPageUrl, { headers: visitHeaders });173 const mainPageRequest = await fetch(mainPageUrl, { headers: visitHeaders });
174174
175 if (!mainPageRequest.ok) {175 if (!mainPageRequest.ok) {
176 console.log('SearXNG request failed', mainPageRequest.statusText);176 console.error('SearXNG request failed', mainPageRequest.statusText);
177 return response.sendStatus(500);177 return response.sendStatus(500);
178 }178 }
179179
@@ -197,14 +197,14 @@ router.post('/searxng', jsonParser, async (request, response) => {
197197
198 if (!searchResult.ok) {198 if (!searchResult.ok) {
199 const text = await searchResult.text();199 const text = await searchResult.text();
200 console.log('SearXNG request failed', searchResult.statusText, text);200 console.error('SearXNG request failed', searchResult.statusText, text);
201 return response.sendStatus(500);201 return response.sendStatus(500);
202 }202 }
203203
204 const data = await searchResult.text();204 const data = await searchResult.text();
205 return response.send(data);205 return response.send(data);
206 } catch (error) {206 } catch (error) {
207 console.log('SearXNG request failed', error);207 console.error('SearXNG request failed', error);
208 return response.sendStatus(500);208 return response.sendStatus(500);
209 }209 }
210});210});
@@ -214,7 +214,7 @@ router.post('/tavily', jsonParser, async (request, response) => {
214 const apiKey = readSecret(request.user.directories, SECRET_KEYS.TAVILY);214 const apiKey = readSecret(request.user.directories, SECRET_KEYS.TAVILY);
215215
216 if (!apiKey) {216 if (!apiKey) {
217 console.log('No Tavily key found');217 console.error('No Tavily key found');
218 return response.sendStatus(400);218 return response.sendStatus(400);
219 }219 }
220220
@@ -241,18 +241,18 @@ router.post('/tavily', jsonParser, async (request, response) => {
241 body: JSON.stringify(body),241 body: JSON.stringify(body),
242 });242 });
243243
244 console.log('Tavily query', query);244 console.debug('Tavily query', query);
245245
246 if (!result.ok) {246 if (!result.ok) {
247 const text = await result.text();247 const text = await result.text();
248 console.log('Tavily request failed', result.statusText, text);248 console.error('Tavily request failed', result.statusText, text);
249 return response.status(500).send(text);249 return response.status(500).send(text);
250 }250 }
251251
252 const data = await result.json();252 const data = await result.json();
253 return response.json(data);253 return response.json(data);
254 } catch (error) {254 } catch (error) {
255 console.log(error);255 console.error(error);
256 return response.sendStatus(500);256 return response.sendStatus(500);
257 }257 }
258});258});
@@ -262,7 +262,7 @@ router.post('/visit', jsonParser, async (request, response) => {
262 const url = request.body.url;262 const url = request.body.url;
263263
264 if (!url) {264 if (!url) {
265 console.log('No url provided for /visit');265 console.error('No url provided for /visit');
266 return response.sendStatus(400);266 return response.sendStatus(400);
267 }267 }
268268
@@ -289,29 +289,29 @@ router.post('/visit', jsonParser, async (request, response) => {
289 throw new Error('Invalid hostname');289 throw new Error('Invalid hostname');
290 }290 }
291 } catch (error) {291 } catch (error) {
292 console.log('Invalid url provided for /visit', url);292 console.error('Invalid url provided for /visit', url);
293 return response.sendStatus(400);293 return response.sendStatus(400);
294 }294 }
295295
296 console.log('Visiting web URL', url);296 console.info('Visiting web URL', url);
297297
298 const result = await fetch(url, { headers: visitHeaders });298 const result = await fetch(url, { headers: visitHeaders });
299299
300 if (!result.ok) {300 if (!result.ok) {
301 console.log(`Visit failed ${result.status} ${result.statusText}`);301 console.error(`Visit failed ${result.status} ${result.statusText}`);
302 return response.sendStatus(500);302 return response.sendStatus(500);
303 }303 }
304304
305 const contentType = String(result.headers.get('content-type'));305 const contentType = String(result.headers.get('content-type'));
306 if (!contentType.includes('text/html')) {306 if (!contentType.includes('text/html')) {
307 console.log(`Visit failed, content-type is ${contentType}, expected text/html`);307 console.error(`Visit failed, content-type is ${contentType}, expected text/html`);
308 return response.sendStatus(500);308 return response.sendStatus(500);
309 }309 }
310310
311 const text = await result.text();311 const text = await result.text();
312 return response.send(text);312 return response.send(text);
313 } catch (error) {313 } catch (error) {
314 console.log(error);314 console.error(error);
315 return response.sendStatus(500);315 return response.sendStatus(500);
316 }316 }
317});317});
src/endpoints/secrets.js+1 -1
@@ -151,7 +151,7 @@ export function getAllSecrets(directories) {
151 const filePath = path.join(directories.root, SECRETS_FILE);151 const filePath = path.join(directories.root, SECRETS_FILE);
152152
153 if (!fs.existsSync(filePath)) {153 if (!fs.existsSync(filePath)) {
154 console.log('Secrets file does not exist');154 console.error('Secrets file does not exist');
155 return undefined;155 return undefined;
156 }156 }
157157
src/endpoints/settings.js+7 -7
@@ -104,7 +104,7 @@ function readPresetsFromDirectory(directoryPath, options = {}) {
104 fileNames.push(removeFileExtension ? item.replace(/\.[^/.]+$/, '') : item);104 fileNames.push(removeFileExtension ? item.replace(/\.[^/.]+$/, '') : item);
105 } catch {105 } catch {
106 // skip106 // skip
107 console.log(`${item} is not a valid JSON`);107 console.warn(`${item} is not a valid JSON`);
108 }108 }
109 });109 });
110110
@@ -119,7 +119,7 @@ async function backupSettings() {
119 backupUserSettings(handle, true);119 backupUserSettings(handle, true);
120 }120 }
121 } catch (err) {121 } catch (err) {
122 console.log('Could not backup settings file', err);122 console.error('Could not backup settings file', err);
123 }123 }
124}124}
125125
@@ -201,7 +201,7 @@ router.post('/save', jsonParser, function (request, response) {
201 triggerAutoSave(request.user.profile.handle);201 triggerAutoSave(request.user.profile.handle);
202 response.send({ result: 'ok' });202 response.send({ result: 'ok' });
203 } catch (err) {203 } catch (err) {
204 console.log(err);204 console.error(err);
205 response.send(err);205 response.send(err);
206 }206 }
207});207});
@@ -291,7 +291,7 @@ router.post('/get-snapshots', jsonParser, async (request, response) => {
291291
292 response.json(result);292 response.json(result);
293 } catch (error) {293 } catch (error) {
294 console.log(error);294 console.error(error);
295 response.sendStatus(500);295 response.sendStatus(500);
296 }296 }
297});297});
@@ -315,7 +315,7 @@ router.post('/load-snapshot', jsonParser, async (request, response) => {
315315
316 response.send(content);316 response.send(content);
317 } catch (error) {317 } catch (error) {
318 console.log(error);318 console.error(error);
319 response.sendStatus(500);319 response.sendStatus(500);
320 }320 }
321});321});
@@ -325,7 +325,7 @@ router.post('/make-snapshot', jsonParser, async (request, response) => {
325 backupUserSettings(request.user.profile.handle, false);325 backupUserSettings(request.user.profile.handle, false);
326 response.sendStatus(204);326 response.sendStatus(204);
327 } catch (error) {327 } catch (error) {
328 console.log(error);328 console.error(error);
329 response.sendStatus(500);329 response.sendStatus(500);
330 }330 }
331});331});
@@ -351,7 +351,7 @@ router.post('/restore-snapshot', jsonParser, async (request, response) => {
351351
352 response.sendStatus(204);352 response.sendStatus(204);
353 } catch (error) {353 } catch (error) {
354 console.log(error);354 console.error(error);
355 response.sendStatus(500);355 response.sendStatus(500);
356 }356 }
357});357});
src/endpoints/speech.js+3 -3
@@ -43,8 +43,8 @@ router.post('/recognize', jsonParser, async (req, res) => {
43 const start = performance.now();43 const start = performance.now();
44 const result = await pipe(wav, { language: lang || null, task: 'transcribe' });44 const result = await pipe(wav, { language: lang || null, task: 'transcribe' });
45 const end = performance.now();45 const end = performance.now();
46 console.log(`Execution duration: ${(end - start) / 1000} seconds`);46 console.debug(`Execution duration: ${(end - start) / 1000} seconds`);
47 console.log('Transcribed audio:', result.text);47 console.debug('Transcribed audio:', result.text);
4848
49 return res.json({ text: result.text });49 return res.json({ text: result.text });
50 } catch (error) {50 } catch (error) {
@@ -64,7 +64,7 @@ router.post('/synthesize', jsonParser, async (req, res) => {
64 const start = performance.now();64 const start = performance.now();
65 const result = await pipe(text, { speaker_embeddings: speaker_embeddings });65 const result = await pipe(text, { speaker_embeddings: speaker_embeddings });
66 const end = performance.now();66 const end = performance.now();
67 console.log(`Execution duration: ${(end - start) / 1000} seconds`);67 console.debug(`Execution duration: ${(end - start) / 1000} seconds`);
6868
69 const wav = new wavefile.WaveFile();69 const wav = new wavefile.WaveFile();
70 wav.fromScratch(1, result.sampling_rate, '32f', result.audio);70 wav.fromScratch(1, result.sampling_rate, '32f', result.audio);
src/endpoints/sprites.js+3 -3
@@ -82,14 +82,14 @@ export function importRisuSprites(directories, data) {
82 return;82 return;
83 }83 }
8484
85 console.log(`RisuAI: Found ${images.length} sprites for ${name}. Writing to disk.`);85 console.info(`RisuAI: Found ${images.length} sprites for ${name}. Writing to disk.`);
86 const files = fs.readdirSync(spritesPath);86 const files = fs.readdirSync(spritesPath);
8787
88 outer: for (const [label, fileBase64] of images) {88 outer: for (const [label, fileBase64] of images) {
89 // Remove existing sprite with the same label89 // Remove existing sprite with the same label
90 for (const file of files) {90 for (const file of files) {
91 if (path.parse(file).name === label) {91 if (path.parse(file).name === label) {
92 console.log(`RisuAI: The sprite ${label} for ${name} already exists. Skipping.`);92 console.warn(`RisuAI: The sprite ${label} for ${name} already exists. Skipping.`);
93 continue outer;93 continue outer;
94 }94 }
95 }95 }
@@ -133,7 +133,7 @@ router.get('/get', jsonParser, function (request, response) {
133 }133 }
134 }134 }
135 catch (err) {135 catch (err) {
136 console.log(err);136 console.error(err);
137 }137 }
138 return response.send(sprites);138 return response.send(sprites);
139});139});
src/endpoints/stable-diffusion.js+74 -74
@@ -45,7 +45,7 @@ router.post('/ping', jsonParser, async (request, response) => {
4545
46 return response.sendStatus(200);46 return response.sendStatus(200);
47 } catch (error) {47 } catch (error) {
48 console.log(error);48 console.error(error);
49 return response.sendStatus(500);49 return response.sendStatus(500);
50 }50 }
51});51});
@@ -99,7 +99,7 @@ router.post('/upscalers', jsonParser, async (request, response) => {
9999
100 return response.send(upscalers);100 return response.send(upscalers);
101 } catch (error) {101 } catch (error) {
102 console.log(error);102 console.error(error);
103 return response.sendStatus(500);103 return response.sendStatus(500);
104 }104 }
105});105});
@@ -131,7 +131,7 @@ router.post('/vaes', jsonParser, async (request, response) => {
131 const names = data.map(x => x.model_name);131 const names = data.map(x => x.model_name);
132 return response.send(names);132 return response.send(names);
133 } catch (error) {133 } catch (error) {
134 console.log(error);134 console.error(error);
135 return response.sendStatus(500);135 return response.sendStatus(500);
136 }136 }
137});137});
@@ -158,7 +158,7 @@ router.post('/samplers', jsonParser, async (request, response) => {
158 return response.send(names);158 return response.send(names);
159159
160 } catch (error) {160 } catch (error) {
161 console.log(error);161 console.error(error);
162 return response.sendStatus(500);162 return response.sendStatus(500);
163 }163 }
164});164});
@@ -184,7 +184,7 @@ router.post('/schedulers', jsonParser, async (request, response) => {
184 const names = data.map(x => x.name);184 const names = data.map(x => x.name);
185 return response.send(names);185 return response.send(names);
186 } catch (error) {186 } catch (error) {
187 console.log(error);187 console.error(error);
188 return response.sendStatus(500);188 return response.sendStatus(500);
189 }189 }
190});190});
@@ -210,7 +210,7 @@ router.post('/models', jsonParser, async (request, response) => {
210 const models = data.map(x => ({ value: x.title, text: x.title }));210 const models = data.map(x => ({ value: x.title, text: x.title }));
211 return response.send(models);211 return response.send(models);
212 } catch (error) {212 } catch (error) {
213 console.log(error);213 console.error(error);
214 return response.sendStatus(500);214 return response.sendStatus(500);
215 }215 }
216});216});
@@ -230,7 +230,7 @@ router.post('/get-model', jsonParser, async (request, response) => {
230 const data = await result.json();230 const data = await result.json();
231 return response.send(data['sd_model_checkpoint']);231 return response.send(data['sd_model_checkpoint']);
232 } catch (error) {232 } catch (error) {
233 console.log(error);233 console.error(error);
234 return response.sendStatus(500);234 return response.sendStatus(500);
235 }235 }
236});236});
@@ -283,13 +283,13 @@ router.post('/set-model', jsonParser, async (request, response) => {
283 break;283 break;
284 }284 }
285285
286 console.log(`Waiting for SD WebUI to finish model loading... Progress: ${progress}; Job count: ${jobCount}`);286 console.info(`Waiting for SD WebUI to finish model loading... Progress: ${progress}; Job count: ${jobCount}`);
287 await delay(CHECK_INTERVAL);287 await delay(CHECK_INTERVAL);
288 }288 }
289289
290 return response.sendStatus(200);290 return response.sendStatus(200);
291 } catch (error) {291 } catch (error) {
292 console.log(error);292 console.error(error);
293 return response.sendStatus(500);293 return response.sendStatus(500);
294 }294 }
295});295});
@@ -309,7 +309,7 @@ router.post('/generate', jsonParser, async (request, response) => {
309 }309 }
310 }310 }
311 } catch (error) {311 } catch (error) {
312 console.log('SD WebUI failed to get options:', error);312 console.error('SD WebUI failed to get options:', error);
313 }313 }
314314
315 const controller = new AbortController();315 const controller = new AbortController();
@@ -323,7 +323,7 @@ router.post('/generate', jsonParser, async (request, response) => {
323 controller.abort();323 controller.abort();
324 });324 });
325325
326 console.log('SD WebUI request:', request.body);326 console.debug('SD WebUI request:', request.body);
327 const txt2imgUrl = new URL(request.body.url);327 const txt2imgUrl = new URL(request.body.url);
328 txt2imgUrl.pathname = '/sdapi/v1/txt2img';328 txt2imgUrl.pathname = '/sdapi/v1/txt2img';
329 const result = await fetch(txt2imgUrl, {329 const result = await fetch(txt2imgUrl, {
@@ -344,7 +344,7 @@ router.post('/generate', jsonParser, async (request, response) => {
344 const data = await result.json();344 const data = await result.json();
345 return response.send(data);345 return response.send(data);
346 } catch (error) {346 } catch (error) {
347 console.log(error);347 console.error(error);
348 return response.sendStatus(500);348 return response.sendStatus(500);
349 }349 }
350});350});
@@ -377,7 +377,7 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => {
377377
378 return response.send(names);378 return response.send(names);
379 } catch (error) {379 } catch (error) {
380 console.log(error);380 console.error(error);
381 return response.sendStatus(500);381 return response.sendStatus(500);
382 }382 }
383});383});
@@ -395,7 +395,7 @@ comfy.post('/ping', jsonParser, async (request, response) => {
395395
396 return response.sendStatus(200);396 return response.sendStatus(200);
397 } catch (error) {397 } catch (error) {
398 console.log(error);398 console.error(error);
399 return response.sendStatus(500);399 return response.sendStatus(500);
400 }400 }
401});401});
@@ -413,7 +413,7 @@ comfy.post('/samplers', jsonParser, async (request, response) => {
413 const data = await result.json();413 const data = await result.json();
414 return response.send(data.KSampler.input.required.sampler_name[0]);414 return response.send(data.KSampler.input.required.sampler_name[0]);
415 } catch (error) {415 } catch (error) {
416 console.log(error);416 console.error(error);
417 return response.sendStatus(500);417 return response.sendStatus(500);
418 }418 }
419});419});
@@ -441,7 +441,7 @@ comfy.post('/models', jsonParser, async (request, response) => {
441441
442 return response.send(models);442 return response.send(models);
443 } catch (error) {443 } catch (error) {
444 console.log(error);444 console.error(error);
445 return response.sendStatus(500);445 return response.sendStatus(500);
446 }446 }
447});447});
@@ -459,7 +459,7 @@ comfy.post('/schedulers', jsonParser, async (request, response) => {
459 const data = await result.json();459 const data = await result.json();
460 return response.send(data.KSampler.input.required.scheduler[0]);460 return response.send(data.KSampler.input.required.scheduler[0]);
461 } catch (error) {461 } catch (error) {
462 console.log(error);462 console.error(error);
463 return response.sendStatus(500);463 return response.sendStatus(500);
464 }464 }
465});465});
@@ -477,7 +477,7 @@ comfy.post('/vaes', jsonParser, async (request, response) => {
477 const data = await result.json();477 const data = await result.json();
478 return response.send(data.VAELoader.input.required.vae_name[0]);478 return response.send(data.VAELoader.input.required.vae_name[0]);
479 } catch (error) {479 } catch (error) {
480 console.log(error);480 console.error(error);
481 return response.sendStatus(500);481 return response.sendStatus(500);
482 }482 }
483});483});
@@ -487,7 +487,7 @@ comfy.post('/workflows', jsonParser, async (request, response) => {
487 const data = getComfyWorkflows(request.user.directories);487 const data = getComfyWorkflows(request.user.directories);
488 return response.send(data);488 return response.send(data);
489 } catch (error) {489 } catch (error) {
490 console.log(error);490 console.error(error);
491 return response.sendStatus(500);491 return response.sendStatus(500);
492 }492 }
493});493});
@@ -501,7 +501,7 @@ comfy.post('/workflow', jsonParser, async (request, response) => {
501 const data = fs.readFileSync(filePath, { encoding: 'utf-8' });501 const data = fs.readFileSync(filePath, { encoding: 'utf-8' });
502 return response.send(JSON.stringify(data));502 return response.send(JSON.stringify(data));
503 } catch (error) {503 } catch (error) {
504 console.log(error);504 console.error(error);
505 return response.sendStatus(500);505 return response.sendStatus(500);
506 }506 }
507});507});
@@ -513,7 +513,7 @@ comfy.post('/save-workflow', jsonParser, async (request, response) => {
513 const data = getComfyWorkflows(request.user.directories);513 const data = getComfyWorkflows(request.user.directories);
514 return response.send(data);514 return response.send(data);
515 } catch (error) {515 } catch (error) {
516 console.log(error);516 console.error(error);
517 return response.sendStatus(500);517 return response.sendStatus(500);
518 }518 }
519});519});
@@ -526,7 +526,7 @@ comfy.post('/delete-workflow', jsonParser, async (request, response) => {
526 }526 }
527 return response.sendStatus(200);527 return response.sendStatus(200);
528 } catch (error) {528 } catch (error) {
529 console.log(error);529 console.error(error);
530 return response.sendStatus(500);530 return response.sendStatus(500);
531 }531 }
532});532});
@@ -591,7 +591,7 @@ comfy.post('/generate', jsonParser, async (request, response) => {
591 const imgBuffer = await imgResponse.arrayBuffer();591 const imgBuffer = await imgResponse.arrayBuffer();
592 return response.send(Buffer.from(imgBuffer).toString('base64'));592 return response.send(Buffer.from(imgBuffer).toString('base64'));
593 } catch (error) {593 } catch (error) {
594 console.log('ComfyUI error:', error);594 console.error('ComfyUI error:', error);
595 response.status(500).send(error.message);595 response.status(500).send(error.message);
596 return response;596 return response;
597 }597 }
@@ -604,7 +604,7 @@ together.post('/models', jsonParser, async (request, response) => {
604 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);604 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);
605605
606 if (!key) {606 if (!key) {
607 console.log('TogetherAI key not found.');607 console.error('TogetherAI key not found.');
608 return response.sendStatus(400);608 return response.sendStatus(400);
609 }609 }
610610
@@ -616,14 +616,14 @@ together.post('/models', jsonParser, async (request, response) => {
616 });616 });
617617
618 if (!modelsResponse.ok) {618 if (!modelsResponse.ok) {
619 console.log('TogetherAI returned an error.');619 console.error('TogetherAI returned an error.');
620 return response.sendStatus(500);620 return response.sendStatus(500);
621 }621 }
622622
623 const data = await modelsResponse.json();623 const data = await modelsResponse.json();
624624
625 if (!Array.isArray(data)) {625 if (!Array.isArray(data)) {
626 console.log('TogetherAI returned invalid data.');626 console.error('TogetherAI returned invalid data.');
627 return response.sendStatus(500);627 return response.sendStatus(500);
628 }628 }
629629
@@ -633,7 +633,7 @@ together.post('/models', jsonParser, async (request, response) => {
633633
634 return response.send(models);634 return response.send(models);
635 } catch (error) {635 } catch (error) {
636 console.log(error);636 console.error(error);
637 return response.sendStatus(500);637 return response.sendStatus(500);
638 }638 }
639});639});
@@ -643,11 +643,11 @@ together.post('/generate', jsonParser, async (request, response) => {
643 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);643 const key = readSecret(request.user.directories, SECRET_KEYS.TOGETHERAI);
644644
645 if (!key) {645 if (!key) {
646 console.log('TogetherAI key not found.');646 console.error('TogetherAI key not found.');
647 return response.sendStatus(400);647 return response.sendStatus(400);
648 }648 }
649649
650 console.log('TogetherAI request:', request.body);650 console.debug('TogetherAI request:', request.body);
651651
652 const result = await fetch('https://api.together.xyz/v1/images/generations', {652 const result = await fetch('https://api.together.xyz/v1/images/generations', {
653 method: 'POST',653 method: 'POST',
@@ -669,13 +669,13 @@ together.post('/generate', jsonParser, async (request, response) => {
669 });669 });
670670
671 if (!result.ok) {671 if (!result.ok) {
672 console.log('TogetherAI returned an error.', { body: await result.text() });672 console.error('TogetherAI returned an error.', { body: await result.text() });
673 return response.sendStatus(500);673 return response.sendStatus(500);
674 }674 }
675675
676 /** @type {any} */676 /** @type {any} */
677 const data = await result.json();677 const data = await result.json();
678 console.log('TogetherAI response:', data);678 console.debug('TogetherAI response:', data);
679679
680 const choice = data?.data?.[0];680 const choice = data?.data?.[0];
681 let b64_json = choice.b64_json;681 let b64_json = choice.b64_json;
@@ -687,7 +687,7 @@ together.post('/generate', jsonParser, async (request, response) => {
687687
688 return response.send({ format: 'jpg', data: b64_json });688 return response.send({ format: 'jpg', data: b64_json });
689 } catch (error) {689 } catch (error) {
690 console.log(error);690 console.error(error);
691 return response.sendStatus(500);691 return response.sendStatus(500);
692 }692 }
693});693});
@@ -709,7 +709,7 @@ drawthings.post('/ping', jsonParser, async (request, response) => {
709709
710 return response.sendStatus(200);710 return response.sendStatus(200);
711 } catch (error) {711 } catch (error) {
712 console.log(error);712 console.error(error);
713 return response.sendStatus(500);713 return response.sendStatus(500);
714 }714 }
715});715});
@@ -728,7 +728,7 @@ drawthings.post('/get-model', jsonParser, async (request, response) => {
728728
729 return response.send(data['model']);729 return response.send(data['model']);
730 } catch (error) {730 } catch (error) {
731 console.log(error);731 console.error(error);
732 return response.sendStatus(500);732 return response.sendStatus(500);
733 }733 }
734});734});
@@ -747,14 +747,14 @@ drawthings.post('/get-upscaler', jsonParser, async (request, response) => {
747747
748 return response.send(data['upscaler']);748 return response.send(data['upscaler']);
749 } catch (error) {749 } catch (error) {
750 console.log(error);750 console.error(error);
751 return response.sendStatus(500);751 return response.sendStatus(500);
752 }752 }
753});753});
754754
755drawthings.post('/generate', jsonParser, async (request, response) => {755drawthings.post('/generate', jsonParser, async (request, response) => {
756 try {756 try {
757 console.log('SD DrawThings API request:', request.body);757 console.debug('SD DrawThings API request:', request.body);
758758
759 const url = new URL(request.body.url);759 const url = new URL(request.body.url);
760 url.pathname = '/sdapi/v1/txt2img';760 url.pathname = '/sdapi/v1/txt2img';
@@ -781,7 +781,7 @@ drawthings.post('/generate', jsonParser, async (request, response) => {
781 const data = await result.json();781 const data = await result.json();
782 return response.send(data);782 return response.send(data);
783 } catch (error) {783 } catch (error) {
784 console.log(error);784 console.error(error);
785 return response.sendStatus(500);785 return response.sendStatus(500);
786 }786 }
787});787});
@@ -794,21 +794,21 @@ pollinations.post('/models', jsonParser, async (_request, response) => {
794 const result = await fetch(modelsUrl);794 const result = await fetch(modelsUrl);
795795
796 if (!result.ok) {796 if (!result.ok) {
797 console.log('Pollinations returned an error.', result.status, result.statusText);797 console.error('Pollinations returned an error.', result.status, result.statusText);
798 throw new Error('Pollinations request failed.');798 throw new Error('Pollinations request failed.');
799 }799 }
800800
801 const data = await result.json();801 const data = await result.json();
802802
803 if (!Array.isArray(data)) {803 if (!Array.isArray(data)) {
804 console.log('Pollinations returned invalid data.');804 console.error('Pollinations returned invalid data.');
805 throw new Error('Pollinations request failed.');805 throw new Error('Pollinations request failed.');
806 }806 }
807807
808 const models = data.map(x => ({ value: x, text: x }));808 const models = data.map(x => ({ value: x, text: x }));
809 return response.send(models);809 return response.send(models);
810 } catch (error) {810 } catch (error) {
811 console.log(error);811 console.error(error);
812 return response.sendStatus(500);812 return response.sendStatus(500);
813 }813 }
814});814});
@@ -829,12 +829,12 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
829 });829 });
830 promptUrl.search = params.toString();830 promptUrl.search = params.toString();
831831
832 console.log('Pollinations request URL:', promptUrl.toString());832 console.info('Pollinations request URL:', promptUrl.toString());
833833
834 const result = await fetch(promptUrl);834 const result = await fetch(promptUrl);
835835
836 if (!result.ok) {836 if (!result.ok) {
837 console.log('Pollinations returned an error.', result.status, result.statusText);837 console.error('Pollinations returned an error.', result.status, result.statusText);
838 throw new Error('Pollinations request failed.');838 throw new Error('Pollinations request failed.');
839 }839 }
840840
@@ -843,7 +843,7 @@ pollinations.post('/generate', jsonParser, async (request, response) => {
843843
844 return response.send({ image: base64 });844 return response.send({ image: base64 });
845 } catch (error) {845 } catch (error) {
846 console.log(error);846 console.error(error);
847 return response.sendStatus(500);847 return response.sendStatus(500);
848 }848 }
849});849});
@@ -855,13 +855,13 @@ stability.post('/generate', jsonParser, async (request, response) => {
855 const key = readSecret(request.user.directories, SECRET_KEYS.STABILITY);855 const key = readSecret(request.user.directories, SECRET_KEYS.STABILITY);
856856
857 if (!key) {857 if (!key) {
858 console.log('Stability AI key not found.');858 console.error('Stability AI key not found.');
859 return response.sendStatus(400);859 return response.sendStatus(400);
860 }860 }
861861
862 const { payload, model } = request.body;862 const { payload, model } = request.body;
863863
864 console.log('Stability AI request:', model, payload);864 console.debug('Stability AI request:', model, payload);
865865
866 const formData = new FormData();866 const formData = new FormData();
867 for (const [key, value] of Object.entries(payload)) {867 for (const [key, value] of Object.entries(payload)) {
@@ -896,14 +896,14 @@ stability.post('/generate', jsonParser, async (request, response) => {
896896
897 if (!result.ok) {897 if (!result.ok) {
898 const text = await result.text();898 const text = await result.text();
899 console.log('Stability AI returned an error.', result.status, result.statusText, text);899 console.error('Stability AI returned an error.', result.status, result.statusText, text);
900 return response.sendStatus(500);900 return response.sendStatus(500);
901 }901 }
902902
903 const buffer = await result.arrayBuffer();903 const buffer = await result.arrayBuffer();
904 return response.send(Buffer.from(buffer).toString('base64'));904 return response.send(Buffer.from(buffer).toString('base64'));
905 } catch (error) {905 } catch (error) {
906 console.log(error);906 console.error(error);
907 return response.sendStatus(500);907 return response.sendStatus(500);
908 }908 }
909});909});
@@ -915,7 +915,7 @@ blockentropy.post('/models', jsonParser, async (request, response) => {
915 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);915 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
916916
917 if (!key) {917 if (!key) {
918 console.log('Block Entropy key not found.');918 console.error('Block Entropy key not found.');
919 return response.sendStatus(400);919 return response.sendStatus(400);
920 }920 }
921921
@@ -927,21 +927,21 @@ blockentropy.post('/models', jsonParser, async (request, response) => {
927 });927 });
928928
929 if (!modelsResponse.ok) {929 if (!modelsResponse.ok) {
930 console.log('Block Entropy returned an error.');930 console.error('Block Entropy returned an error.');
931 return response.sendStatus(500);931 return response.sendStatus(500);
932 }932 }
933933
934 const data = await modelsResponse.json();934 const data = await modelsResponse.json();
935935
936 if (!Array.isArray(data)) {936 if (!Array.isArray(data)) {
937 console.log('Block Entropy returned invalid data.');937 console.error('Block Entropy returned invalid data.');
938 return response.sendStatus(500);938 return response.sendStatus(500);
939 }939 }
940 const models = data.map(x => ({ value: x.name, text: x.name }));940 const models = data.map(x => ({ value: x.name, text: x.name }));
941 return response.send(models);941 return response.send(models);
942942
943 } catch (error) {943 } catch (error) {
944 console.log(error);944 console.error(error);
945 return response.sendStatus(500);945 return response.sendStatus(500);
946 }946 }
947});947});
@@ -951,11 +951,11 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
951 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);951 const key = readSecret(request.user.directories, SECRET_KEYS.BLOCKENTROPY);
952952
953 if (!key) {953 if (!key) {
954 console.log('Block Entropy key not found.');954 console.error('Block Entropy key not found.');
955 return response.sendStatus(400);955 return response.sendStatus(400);
956 }956 }
957957
958 console.log('Block Entropy request:', request.body);958 console.debug('Block Entropy request:', request.body);
959959
960 const result = await fetch('https://api.blockentropy.ai/sdapi/v1/txt2img', {960 const result = await fetch('https://api.blockentropy.ai/sdapi/v1/txt2img', {
961 method: 'POST',961 method: 'POST',
@@ -976,16 +976,16 @@ blockentropy.post('/generate', jsonParser, async (request, response) => {
976 });976 });
977977
978 if (!result.ok) {978 if (!result.ok) {
979 console.log('Block Entropy returned an error.');979 console.error('Block Entropy returned an error.');
980 return response.sendStatus(500);980 return response.sendStatus(500);
981 }981 }
982982
983 const data = await result.json();983 const data = await result.json();
984 console.log('Block Entropy response:', data);984 console.debug('Block Entropy response:', data);
985985
986 return response.send(data);986 return response.send(data);
987 } catch (error) {987 } catch (error) {
988 console.log(error);988 console.error(error);
989 return response.sendStatus(500);989 return response.sendStatus(500);
990 }990 }
991});991});
@@ -998,11 +998,11 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
998 const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);998 const key = readSecret(request.user.directories, SECRET_KEYS.HUGGINGFACE);
999999
1000 if (!key) {1000 if (!key) {
1001 console.log('Hugging Face key not found.');1001 console.error('Hugging Face key not found.');
1002 return response.sendStatus(400);1002 return response.sendStatus(400);
1003 }1003 }
10041004
1005 console.log('Hugging Face request:', request.body);1005 console.debug('Hugging Face request:', request.body);
10061006
1007 const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {1007 const result = await fetch(`https://api-inference.huggingface.co/models/${request.body.model}`, {
1008 method: 'POST',1008 method: 'POST',
@@ -1016,7 +1016,7 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
1016 });1016 });
10171017
1018 if (!result.ok) {1018 if (!result.ok) {
1019 console.log('Hugging Face returned an error.');1019 console.error('Hugging Face returned an error.');
1020 return response.sendStatus(500);1020 return response.sendStatus(500);
1021 }1021 }
10221022
@@ -1025,7 +1025,7 @@ huggingface.post('/generate', jsonParser, async (request, response) => {
1025 image: Buffer.from(buffer).toString('base64'),1025 image: Buffer.from(buffer).toString('base64'),
1026 });1026 });
1027 } catch (error) {1027 } catch (error) {
1028 console.log(error);1028 console.error(error);
1029 return response.sendStatus(500);1029 return response.sendStatus(500);
1030 }1030 }
1031});1031});
@@ -1037,7 +1037,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1037 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);1037 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
10381038
1039 if (!key) {1039 if (!key) {
1040 console.log('NanoGPT key not found.');1040 console.error('NanoGPT key not found.');
1041 return response.sendStatus(400);1041 return response.sendStatus(400);
1042 }1042 }
10431043
@@ -1050,7 +1050,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1050 });1050 });
10511051
1052 if (!modelsResponse.ok) {1052 if (!modelsResponse.ok) {
1053 console.log('NanoGPT returned an error.');1053 console.error('NanoGPT returned an error.');
1054 return response.sendStatus(500);1054 return response.sendStatus(500);
1055 }1055 }
10561056
@@ -1059,7 +1059,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1059 const imageModels = data?.models?.image;1059 const imageModels = data?.models?.image;
10601060
1061 if (!imageModels || typeof imageModels !== 'object') {1061 if (!imageModels || typeof imageModels !== 'object') {
1062 console.log('NanoGPT returned invalid data.');1062 console.error('NanoGPT returned invalid data.');
1063 return response.sendStatus(500);1063 return response.sendStatus(500);
1064 }1064 }
10651065
@@ -1067,7 +1067,7 @@ nanogpt.post('/models', jsonParser, async (request, response) => {
1067 return response.send(models);1067 return response.send(models);
1068 }1068 }
1069 catch (error) {1069 catch (error) {
1070 console.log(error);1070 console.error(error);
1071 return response.sendStatus(500);1071 return response.sendStatus(500);
1072 }1072 }
1073});1073});
@@ -1077,11 +1077,11 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
1077 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);1077 const key = readSecret(request.user.directories, SECRET_KEYS.NANOGPT);
10781078
1079 if (!key) {1079 if (!key) {
1080 console.log('NanoGPT key not found.');1080 console.error('NanoGPT key not found.');
1081 return response.sendStatus(400);1081 return response.sendStatus(400);
1082 }1082 }
10831083
1084 console.log('NanoGPT request:', request.body);1084 console.debug('NanoGPT request:', request.body);
10851085
1086 const result = await fetch('https://nano-gpt.com/api/generate-image', {1086 const result = await fetch('https://nano-gpt.com/api/generate-image', {
1087 method: 'POST',1087 method: 'POST',
@@ -1093,7 +1093,7 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
1093 });1093 });
10941094
1095 if (!result.ok) {1095 if (!result.ok) {
1096 console.log('NanoGPT returned an error.');1096 console.error('NanoGPT returned an error.');
1097 return response.sendStatus(500);1097 return response.sendStatus(500);
1098 }1098 }
10991099
@@ -1102,14 +1102,14 @@ nanogpt.post('/generate', jsonParser, async (request, response) => {
11021102
1103 const image = data?.data?.[0]?.b64_json;1103 const image = data?.data?.[0]?.b64_json;
1104 if (!image) {1104 if (!image) {
1105 console.log('NanoGPT returned invalid data.');1105 console.error('NanoGPT returned invalid data.');
1106 return response.sendStatus(500);1106 return response.sendStatus(500);
1107 }1107 }
11081108
1109 return response.send({ image });1109 return response.send({ image });
1110 }1110 }
1111 catch (error) {1111 catch (error) {
1112 console.log(error);1112 console.error(error);
1113 return response.sendStatus(500);1113 return response.sendStatus(500);
1114 }1114 }
1115});1115});
@@ -1121,7 +1121,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1121 const key = readSecret(request.user.directories, SECRET_KEYS.BFL);1121 const key = readSecret(request.user.directories, SECRET_KEYS.BFL);
11221122
1123 if (!key) {1123 if (!key) {
1124 console.log('BFL key not found.');1124 console.error('BFL key not found.');
1125 return response.sendStatus(400);1125 return response.sendStatus(400);
1126 }1126 }
11271127
@@ -1173,7 +1173,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1173 delete requestBody.guidance;1173 delete requestBody.guidance;
1174 }1174 }
11751175
1176 console.log('BFL request:', requestBody);1176 console.debug('BFL request:', requestBody);
11771177
1178 const result = await fetch(`https://api.bfl.ml/v1/${request.body.model}`, {1178 const result = await fetch(`https://api.bfl.ml/v1/${request.body.model}`, {
1179 method: 'POST',1179 method: 'POST',
@@ -1185,7 +1185,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1185 });1185 });
11861186
1187 if (!result.ok) {1187 if (!result.ok) {
1188 console.log('BFL returned an error.');1188 console.error('BFL returned an error.');
1189 return response.sendStatus(500);1189 return response.sendStatus(500);
1190 }1190 }
11911191
@@ -1201,7 +1201,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
12011201
1202 if (!statusResult.ok) {1202 if (!statusResult.ok) {
1203 const text = await statusResult.text();1203 const text = await statusResult.text();
1204 console.log('BFL returned an error.', text);1204 console.error('BFL returned an error.', text);
1205 return response.sendStatus(500);1205 return response.sendStatus(500);
1206 }1206 }
12071207
@@ -1223,7 +1223,7 @@ bfl.post('/generate', jsonParser, async (request, response) => {
1223 throw new Error('BFL failed to generate image.', { cause: statusData });1223 throw new Error('BFL failed to generate image.', { cause: statusData });
1224 }1224 }
1225 } catch (error) {1225 } catch (error) {
1226 console.log(error);1226 console.error(error);
1227 return response.sendStatus(500);1227 return response.sendStatus(500);
1228 }1228 }
1229});1229});
src/endpoints/stats.js+2 -2
@@ -148,7 +148,7 @@ async function collectAndCreateStats(chatsPath, charactersPath) {
148 * @param {string} charactersPath Path to the directory containing the character files.148 * @param {string} charactersPath Path to the directory containing the character files.
149 */149 */
150export async function recreateStats(handle, chatsPath, charactersPath) {150export async function recreateStats(handle, chatsPath, charactersPath) {
151 console.log('Collecting and creating stats for user:', handle);151 console.info('Collecting and creating stats for user:', handle);
152 const stats = await collectAndCreateStats(chatsPath, charactersPath);152 const stats = await collectAndCreateStats(chatsPath, charactersPath);
153 STATS.set(handle, stats);153 STATS.set(handle, stats);
154 await saveStatsToFile();154 await saveStatsToFile();
@@ -200,7 +200,7 @@ async function saveStatsToFile() {
200 await writeFileAtomic(statsFilePath, JSON.stringify(charStats));200 await writeFileAtomic(statsFilePath, JSON.stringify(charStats));
201 TIMESTAMPS.set(handle, Date.now());201 TIMESTAMPS.set(handle, Date.now());
202 } catch (error) {202 } catch (error) {
203 console.log('Failed to save stats to file.', error);203 console.error('Failed to save stats to file.', error);
204 }204 }
205 }205 }
206 }206 }
src/endpoints/thumbnails.js+3 -4
@@ -89,7 +89,6 @@ async function generateThumbnail(directories, type, file) {
89 let thumbnailFolder = getThumbnailFolder(directories, type);89 let thumbnailFolder = getThumbnailFolder(directories, type);
90 let originalFolder = getOriginalFolder(directories, type);90 let originalFolder = getOriginalFolder(directories, type);
91 if (thumbnailFolder === undefined || originalFolder === undefined) throw new Error('Invalid thumbnail type');91 if (thumbnailFolder === undefined || originalFolder === undefined) throw new Error('Invalid thumbnail type');
92
93 const pathToCachedFile = path.join(thumbnailFolder, file);92 const pathToCachedFile = path.join(thumbnailFolder, file);
94 const pathToOriginalFile = path.join(originalFolder, file);93 const pathToOriginalFile = path.join(originalFolder, file);
9594
@@ -104,7 +103,7 @@ async function generateThumbnail(directories, type, file) {
104 const cachedStat = fs.statSync(pathToCachedFile);103 const cachedStat = fs.statSync(pathToCachedFile);
105104
106 if (originalStat.mtimeMs > cachedStat.ctimeMs) {105 if (originalStat.mtimeMs > cachedStat.ctimeMs) {
107 //console.log('Original file changed. Regenerating thumbnail...');106 //console.warn('Original file changed. Regenerating thumbnail...');
108 shouldRegenerate = true;107 shouldRegenerate = true;
109 }108 }
110 }109 }
@@ -157,7 +156,7 @@ export async function ensureThumbnailCache() {
157 return;156 return;
158 }157 }
159158
160 console.log('Generating thumbnails cache. Please wait...');159 console.info('Generating thumbnails cache. Please wait...');
161160
162 const bgFiles = fs.readdirSync(directories.backgrounds);161 const bgFiles = fs.readdirSync(directories.backgrounds);
163 const tasks = [];162 const tasks = [];
@@ -167,7 +166,7 @@ export async function ensureThumbnailCache() {
167 }166 }
168167
169 await Promise.all(tasks);168 await Promise.all(tasks);
170 console.log(`Done! Generated: ${bgFiles.length} preview images`);169 console.info(`Done! Generated: ${bgFiles.length} preview images`);
171 }170 }
172}171}
173172
src/endpoints/tokenizers.js+17 -17
@@ -96,7 +96,7 @@ async function getPathToTokenizer(model, fallbackModel) {
96 throw new Error('Downloading tokenizers is disabled, the model is not cached');96 throw new Error('Downloading tokenizers is disabled, the model is not cached');
97 }97 }
9898
99 console.log('Downloading tokenizer model:', model);99 console.info('Downloading tokenizer model:', model);
100 const response = await fetch(model);100 const response = await fetch(model);
101 if (!response.ok) {101 if (!response.ok) {
102 throw new Error(`Failed to fetch the model: ${response.status} ${response.statusText}`);102 throw new Error(`Failed to fetch the model: ${response.status} ${response.statusText}`);
@@ -108,7 +108,7 @@ async function getPathToTokenizer(model, fallbackModel) {
108 } catch (error) {108 } catch (error) {
109 const getLastSegment = str => str?.split('/')?.pop() || '';109 const getLastSegment = str => str?.split('/')?.pop() || '';
110 if (fallbackModel) {110 if (fallbackModel) {
111 console.log(`Could not get a tokenizer from ${getLastSegment(model)}. Reason: ${error.message}. Using a fallback model: ${getLastSegment(fallbackModel)}.`);111 console.error(`Could not get a tokenizer from ${getLastSegment(model)}. Reason: ${error.message}. Using a fallback model: ${getLastSegment(fallbackModel)}.`);
112 return fallbackModel;112 return fallbackModel;
113 }113 }
114114
@@ -156,7 +156,7 @@ class SentencePieceTokenizer {
156 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);156 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);
157 this.#instance = new SentencePieceProcessor();157 this.#instance = new SentencePieceProcessor();
158 await this.#instance.load(pathToModel);158 await this.#instance.load(pathToModel);
159 console.log('Instantiated the tokenizer for', path.parse(pathToModel).name);159 console.info('Instantiated the tokenizer for', path.parse(pathToModel).name);
160 return this.#instance;160 return this.#instance;
161 } catch (error) {161 } catch (error) {
162 console.error('Sentencepiece tokenizer failed to load: ' + this.#model, error);162 console.error('Sentencepiece tokenizer failed to load: ' + this.#model, error);
@@ -205,7 +205,7 @@ class WebTokenizer {
205 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);205 const pathToModel = await getPathToTokenizer(this.#model, this.#fallbackModel);
206 const arrayBuffer = fs.readFileSync(pathToModel).buffer;206 const arrayBuffer = fs.readFileSync(pathToModel).buffer;
207 this.#instance = await Tokenizer.fromJSON(arrayBuffer);207 this.#instance = await Tokenizer.fromJSON(arrayBuffer);
208 console.log('Instantiated the tokenizer for', path.parse(pathToModel).name);208 console.info('Instantiated the tokenizer for', path.parse(pathToModel).name);
209 return this.#instance;209 return this.#instance;
210 } catch (error) {210 } catch (error) {
211 console.error('Web tokenizer failed to load: ' + this.#model, error);211 console.error('Web tokenizer failed to load: ' + this.#model, error);
@@ -442,7 +442,7 @@ export function getTiktokenTokenizer(model) {
442 }442 }
443443
444 const tokenizer = tiktoken.encoding_for_model(model);444 const tokenizer = tiktoken.encoding_for_model(model);
445 console.log('Instantiated the tokenizer for', model);445 console.info('Instantiated the tokenizer for', model);
446 tokenizersCache[model] = tokenizer;446 tokenizersCache[model] = tokenizer;
447 return tokenizer;447 return tokenizer;
448}448}
@@ -489,7 +489,7 @@ function createSentencepieceEncodingHandler(tokenizer) {
489 const chunks = instance?.encodePieces(text);489 const chunks = instance?.encodePieces(text);
490 return response.send({ ids, count, chunks });490 return response.send({ ids, count, chunks });
491 } catch (error) {491 } catch (error) {
492 console.log(error);492 console.error(error);
493 return response.send({ ids: [], count: 0, chunks: [] });493 return response.send({ ids: [], count: 0, chunks: [] });
494 }494 }
495 };495 };
@@ -520,7 +520,7 @@ function createSentencepieceDecodingHandler(tokenizer) {
520 const text = chunks.join('');520 const text = chunks.join('');
521 return response.send({ text, chunks });521 return response.send({ text, chunks });
522 } catch (error) {522 } catch (error) {
523 console.log(error);523 console.error(error);
524 return response.send({ text: '', chunks: [] });524 return response.send({ text: '', chunks: [] });
525 }525 }
526 };526 };
@@ -549,7 +549,7 @@ function createTiktokenEncodingHandler(modelId) {
549 const chunks = await getTiktokenChunks(tokenizer, tokens);549 const chunks = await getTiktokenChunks(tokenizer, tokens);
550 return response.send({ ids: tokens, count: tokens.length, chunks });550 return response.send({ ids: tokens, count: tokens.length, chunks });
551 } catch (error) {551 } catch (error) {
552 console.log(error);552 console.error(error);
553 return response.send({ ids: [], count: 0, chunks: [] });553 return response.send({ ids: [], count: 0, chunks: [] });
554 }554 }
555 };555 };
@@ -578,7 +578,7 @@ function createTiktokenDecodingHandler(modelId) {
578 const text = new TextDecoder().decode(textBytes);578 const text = new TextDecoder().decode(textBytes);
579 return response.send({ text });579 return response.send({ text });
580 } catch (error) {580 } catch (error) {
581 console.log(error);581 console.error(error);
582 return response.send({ text: '' });582 return response.send({ text: '' });
583 }583 }
584 };584 };
@@ -608,7 +608,7 @@ function createWebTokenizerEncodingHandler(tokenizer) {
608 const chunks = getWebTokenizersChunks(instance, tokens);608 const chunks = getWebTokenizersChunks(instance, tokens);
609 return response.send({ ids: tokens, count: tokens.length, chunks });609 return response.send({ ids: tokens, count: tokens.length, chunks });
610 } catch (error) {610 } catch (error) {
611 console.log(error);611 console.error(error);
612 return response.send({ ids: [], count: 0, chunks: [] });612 return response.send({ ids: [], count: 0, chunks: [] });
613 }613 }
614 };614 };
@@ -639,7 +639,7 @@ function createWebTokenizerDecodingHandler(tokenizer) {
639 const text = instance.decode(new Int32Array(ids));639 const text = instance.decode(new Int32Array(ids));
640 return response.send({ text, chunks });640 return response.send({ text, chunks });
641 } catch (error) {641 } catch (error) {
642 console.log(error);642 console.error(error);
643 return response.send({ text: '', chunks: [] });643 return response.send({ text: '', chunks: [] });
644 }644 }
645 };645 };
@@ -739,7 +739,7 @@ router.post('/openai/encode', jsonParser, async function (req, res) {
739 const handler = createTiktokenEncodingHandler(model);739 const handler = createTiktokenEncodingHandler(model);
740 return handler(req, res);740 return handler(req, res);
741 } catch (error) {741 } catch (error) {
742 console.log(error);742 console.error(error);
743 return res.send({ ids: [], count: 0, chunks: [] });743 return res.send({ ids: [], count: 0, chunks: [] });
744 }744 }
745});745});
@@ -807,7 +807,7 @@ router.post('/openai/decode', jsonParser, async function (req, res) {
807 const handler = createTiktokenDecodingHandler(model);807 const handler = createTiktokenDecodingHandler(model);
808 return handler(req, res);808 return handler(req, res);
809 } catch (error) {809 } catch (error) {
810 console.log(error);810 console.error(error);
811 return res.send({ text: '' });811 return res.send({ text: '' });
812 }812 }
813});813});
@@ -946,7 +946,7 @@ router.post('/remote/kobold/count', jsonParser, async function (request, respons
946 const result = await fetch(url, args);946 const result = await fetch(url, args);
947947
948 if (!result.ok) {948 if (!result.ok) {
949 console.log(`API returned error: ${result.status} ${result.statusText}`);949 console.error(`API returned error: ${result.status} ${result.statusText}`);
950 return response.send({ error: true });950 return response.send({ error: true });
951 }951 }
952952
@@ -955,7 +955,7 @@ router.post('/remote/kobold/count', jsonParser, async function (request, respons
955 const ids = data['ids'] ?? [];955 const ids = data['ids'] ?? [];
956 return response.send({ count, ids });956 return response.send({ count, ids });
957 } catch (error) {957 } catch (error) {
958 console.log(error);958 console.error(error);
959 return response.send({ error: true });959 return response.send({ error: true });
960 }960 }
961});961});
@@ -1010,7 +1010,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
1010 const result = await fetch(url, args);1010 const result = await fetch(url, args);
10111011
1012 if (!result.ok) {1012 if (!result.ok) {
1013 console.log(`API returned error: ${result.status} ${result.statusText}`);1013 console.error(`API returned error: ${result.status} ${result.statusText}`);
1014 return response.send({ error: true });1014 return response.send({ error: true });
1015 }1015 }
10161016
@@ -1020,7 +1020,7 @@ router.post('/remote/textgenerationwebui/encode', jsonParser, async function (re
10201020
1021 return response.send({ count, ids });1021 return response.send({ count, ids });
1022 } catch (error) {1022 } catch (error) {
1023 console.log(error);1023 console.error(error);
1024 return response.send({ error: true });1024 return response.send({ error: true });
1025 }1025 }
1026});1026});
src/endpoints/translate.js+31 -31
@@ -35,7 +35,7 @@ function decodeBuffer(buffer) {
35 try {35 try {
36 return iconv.decode(Buffer.from(buffer), 'utf-8');36 return iconv.decode(Buffer.from(buffer), 'utf-8');
37 } catch (error) {37 } catch (error) {
38 console.log('Failed to decode buffer:', error);38 console.warn('Failed to decode buffer:', error);
39 return Buffer.from(buffer).toString('utf-8');39 return Buffer.from(buffer).toString('utf-8');
40 }40 }
41}41}
@@ -46,7 +46,7 @@ router.post('/libre', jsonParser, async (request, response) => {
46 const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL);46 const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL);
4747
48 if (!url) {48 if (!url) {
49 console.log('LibreTranslate URL is not configured.');49 console.error('LibreTranslate URL is not configured.');
50 return response.sendStatus(400);50 return response.sendStatus(400);
51 }51 }
5252
@@ -69,7 +69,7 @@ router.post('/libre', jsonParser, async (request, response) => {
69 return response.sendStatus(400);69 return response.sendStatus(400);
70 }70 }
7171
72 console.log('Input text: ' + text);72 console.debug('Input text: ' + text);
7373
74 const result = await fetch(url, {74 const result = await fetch(url, {
75 method: 'POST',75 method: 'POST',
@@ -85,17 +85,17 @@ router.post('/libre', jsonParser, async (request, response) => {
8585
86 if (!result.ok) {86 if (!result.ok) {
87 const error = await result.text();87 const error = await result.text();
88 console.log('LibreTranslate error: ', result.statusText, error);88 console.error('LibreTranslate error: ', result.statusText, error);
89 return response.sendStatus(500);89 return response.sendStatus(500);
90 }90 }
9191
92 /** @type {any} */92 /** @type {any} */
93 const json = await result.json();93 const json = await result.json();
94 console.log('Translated text: ' + json.translatedText);94 console.debug('Translated text: ' + json.translatedText);
9595
96 return response.send(json.translatedText);96 return response.send(json.translatedText);
97 } catch (error) {97 } catch (error) {
98 console.log('Translation error: ' + error.message);98 console.error('Translation error: ' + error.message);
99 return response.sendStatus(500);99 return response.sendStatus(500);
100 }100 }
101});101});
@@ -109,14 +109,14 @@ router.post('/google', jsonParser, async (request, response) => {
109 return response.sendStatus(400);109 return response.sendStatus(400);
110 }110 }
111111
112 console.log('Input text: ' + text);112 console.debug('Input text: ' + text);
113113
114 const { generateRequestUrl, normaliseResponse } = getGoogleTranslateClient();114 const { generateRequestUrl, normaliseResponse } = getGoogleTranslateClient();
115 const requestUrl = generateRequestUrl(text, { to: lang });115 const requestUrl = generateRequestUrl(text, { to: lang });
116 const result = await fetch(requestUrl);116 const result = await fetch(requestUrl);
117117
118 if (!result.ok) {118 if (!result.ok) {
119 console.log('Google Translate error: ', result.statusText);119 console.error('Google Translate error: ', result.statusText);
120 return response.sendStatus(500);120 return response.sendStatus(500);
121 }121 }
122122
@@ -125,10 +125,10 @@ router.post('/google', jsonParser, async (request, response) => {
125 const translatedText = translateResponse.text;125 const translatedText = translateResponse.text;
126126
127 response.setHeader('Content-Type', 'text/plain; charset=utf-8');127 response.setHeader('Content-Type', 'text/plain; charset=utf-8');
128 console.log('Translated text: ' + translatedText);128 console.debug('Translated text: ' + translatedText);
129 return response.send(translatedText);129 return response.send(translatedText);
130 } catch (error) {130 } catch (error) {
131 console.log('Translation error', error);131 console.error('Translation error', error);
132 return response.sendStatus(500);132 return response.sendStatus(500);
133 }133 }
134});134});
@@ -161,7 +161,7 @@ router.post('/yandex', jsonParser, async (request, response) => {
161 params.append('lang', lang);161 params.append('lang', lang);
162 const ucid = uuidv4().replaceAll('-', '');162 const ucid = uuidv4().replaceAll('-', '');
163163
164 console.log('Input text: ' + inputText);164 console.debug('Input text: ' + inputText);
165165
166 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {166 const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, {
167 method: 'POST',167 method: 'POST',
@@ -173,18 +173,18 @@ router.post('/yandex', jsonParser, async (request, response) => {
173173
174 if (!result.ok) {174 if (!result.ok) {
175 const error = await result.text();175 const error = await result.text();
176 console.log('Yandex error: ', result.statusText, error);176 console.error('Yandex error: ', result.statusText, error);
177 return response.sendStatus(500);177 return response.sendStatus(500);
178 }178 }
179179
180 /** @type {any} */180 /** @type {any} */
181 const json = await result.json();181 const json = await result.json();
182 const translated = json.text.join();182 const translated = json.text.join();
183 console.log('Translated text: ' + translated);183 console.debug('Translated text: ' + translated);
184184
185 return response.send(translated);185 return response.send(translated);
186 } catch (error) {186 } catch (error) {
187 console.log('Translation error: ' + error.message);187 console.error('Translation error: ' + error.message);
188 return response.sendStatus(500);188 return response.sendStatus(500);
189 }189 }
190});190});
@@ -195,7 +195,7 @@ router.post('/lingva', jsonParser, async (request, response) => {
195 const baseUrl = secretUrl || LINGVA_DEFAULT;195 const baseUrl = secretUrl || LINGVA_DEFAULT;
196196
197 if (!secretUrl && baseUrl === LINGVA_DEFAULT) {197 if (!secretUrl && baseUrl === LINGVA_DEFAULT) {
198 console.log('Lingva URL is using default value.', LINGVA_DEFAULT);198 console.warn('Lingva URL is using default value.', LINGVA_DEFAULT);
199 }199 }
200200
201 if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') {201 if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') {
@@ -213,22 +213,22 @@ router.post('/lingva', jsonParser, async (request, response) => {
213 return response.sendStatus(400);213 return response.sendStatus(400);
214 }214 }
215215
216 console.log('Input text: ' + text);216 console.debug('Input text: ' + text);
217217
218 const url = urlJoin(baseUrl, 'auto', lang, encodeURIComponent(text));218 const url = urlJoin(baseUrl, 'auto', lang, encodeURIComponent(text));
219 const result = await fetch(url);219 const result = await fetch(url);
220220
221 if (!result.ok) {221 if (!result.ok) {
222 const error = await result.text();222 const error = await result.text();
223 console.log('Lingva error: ', result.statusText, error);223 console.error('Lingva error: ', result.statusText, error);
224 }224 }
225225
226 /** @type {any} */226 /** @type {any} */
227 const data = await result.json();227 const data = await result.json();
228 console.log('Translated text: ' + data.translation);228 console.debug('Translated text: ' + data.translation);
229 return response.send(data.translation);229 return response.send(data.translation);
230 } catch (error) {230 } catch (error) {
231 console.log('Translation error', error);231 console.error('Translation error', error);
232 return response.sendStatus(500);232 return response.sendStatus(500);
233 }233 }
234});234});
@@ -238,7 +238,7 @@ router.post('/deepl', jsonParser, async (request, response) => {
238 const key = readSecret(request.user.directories, SECRET_KEYS.DEEPL);238 const key = readSecret(request.user.directories, SECRET_KEYS.DEEPL);
239239
240 if (!key) {240 if (!key) {
241 console.log('DeepL key is not configured.');241 console.error('DeepL key is not configured.');
242 return response.sendStatus(400);242 return response.sendStatus(400);
243 }243 }
244244
@@ -254,7 +254,7 @@ router.post('/deepl', jsonParser, async (request, response) => {
254 return response.sendStatus(400);254 return response.sendStatus(400);
255 }255 }
256256
257 console.log('Input text: ' + text);257 console.debug('Input text: ' + text);
258258
259 const params = new URLSearchParams();259 const params = new URLSearchParams();
260 params.append('text', text);260 params.append('text', text);
@@ -280,17 +280,17 @@ router.post('/deepl', jsonParser, async (request, response) => {
280280
281 if (!result.ok) {281 if (!result.ok) {
282 const error = await result.text();282 const error = await result.text();
283 console.log('DeepL error: ', result.statusText, error);283 console.error('DeepL error: ', result.statusText, error);
284 return response.sendStatus(500);284 return response.sendStatus(500);
285 }285 }
286286
287 /** @type {any} */287 /** @type {any} */
288 const json = await result.json();288 const json = await result.json();
289 console.log('Translated text: ' + json.translations[0].text);289 console.debug('Translated text: ' + json.translations[0].text);
290290
291 return response.send(json.translations[0].text);291 return response.send(json.translations[0].text);
292 } catch (error) {292 } catch (error) {
293 console.log('Translation error: ' + error.message);293 console.error('Translation error: ' + error.message);
294 return response.sendStatus(500);294 return response.sendStatus(500);
295 }295 }
296});296});
@@ -376,7 +376,7 @@ router.post('/deeplx', jsonParser, async (request, response) => {
376 return response.sendStatus(400);376 return response.sendStatus(400);
377 }377 }
378378
379 console.log('Input text: ' + text);379 console.debug('Input text: ' + text);
380380
381 const result = await fetch(url, {381 const result = await fetch(url, {
382 method: 'POST',382 method: 'POST',
@@ -393,17 +393,17 @@ router.post('/deeplx', jsonParser, async (request, response) => {
393393
394 if (!result.ok) {394 if (!result.ok) {
395 const error = await result.text();395 const error = await result.text();
396 console.log('DeepLX error: ', result.statusText, error);396 console.error('DeepLX error: ', result.statusText, error);
397 return response.sendStatus(500);397 return response.sendStatus(500);
398 }398 }
399399
400 /** @type {any} */400 /** @type {any} */
401 const json = await result.json();401 const json = await result.json();
402 console.log('Translated text: ' + json.data);402 console.debug('Translated text: ' + json.data);
403403
404 return response.send(json.data);404 return response.send(json.data);
405 } catch (error) {405 } catch (error) {
406 console.log('DeepLX translation error: ' + error.message);406 console.error('DeepLX translation error: ' + error.message);
407 return response.sendStatus(500);407 return response.sendStatus(500);
408 }408 }
409});409});
@@ -429,14 +429,14 @@ router.post('/bing', jsonParser, async (request, response) => {
429 return response.sendStatus(400);429 return response.sendStatus(400);
430 }430 }
431431
432 console.log('Input text: ' + text);432 console.debug('Input text: ' + text);
433433
434 const result = await bingTranslate(text, null, lang);434 const result = await bingTranslate(text, null, lang);
435 const translatedText = result?.translation;435 const translatedText = result?.translation;
436 console.log('Translated text: ' + translatedText);436 console.debug('Translated text: ' + translatedText);
437 return response.send(translatedText);437 return response.send(translatedText);
438 } catch (error) {438 } catch (error) {
439 console.log('Translation error', error);439 console.error('Translation error', error);
440 return response.sendStatus(500);440 return response.sendStatus(500);
441 }441 }
442});442});
src/endpoints/users-admin.js+19 -19
@@ -53,12 +53,12 @@ router.post('/get', requireAdminMiddleware, jsonParser, async (_request, respons
53router.post('/disable', requireAdminMiddleware, jsonParser, async (request, response) => {53router.post('/disable', requireAdminMiddleware, jsonParser, async (request, response) => {
54 try {54 try {
55 if (!request.body.handle) {55 if (!request.body.handle) {
56 console.log('Disable user failed: Missing required fields');56 console.error('Disable user failed: Missing required fields');
57 return response.status(400).json({ error: 'Missing required fields' });57 return response.status(400).json({ error: 'Missing required fields' });
58 }58 }
5959
60 if (request.body.handle === request.user.profile.handle) {60 if (request.body.handle === request.user.profile.handle) {
61 console.log('Disable user failed: Cannot disable yourself');61 console.error('Disable user failed: Cannot disable yourself');
62 return response.status(400).json({ error: 'Cannot disable yourself' });62 return response.status(400).json({ error: 'Cannot disable yourself' });
63 }63 }
6464
@@ -66,7 +66,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp
66 const user = await storage.getItem(toKey(request.body.handle));66 const user = await storage.getItem(toKey(request.body.handle));
6767
68 if (!user) {68 if (!user) {
69 console.log('Disable user failed: User not found');69 console.error('Disable user failed: User not found');
70 return response.status(404).json({ error: 'User not found' });70 return response.status(404).json({ error: 'User not found' });
71 }71 }
7272
@@ -82,7 +82,7 @@ router.post('/disable', requireAdminMiddleware, jsonParser, async (request, resp
82router.post('/enable', requireAdminMiddleware, jsonParser, async (request, response) => {82router.post('/enable', requireAdminMiddleware, jsonParser, async (request, response) => {
83 try {83 try {
84 if (!request.body.handle) {84 if (!request.body.handle) {
85 console.log('Enable user failed: Missing required fields');85 console.error('Enable user failed: Missing required fields');
86 return response.status(400).json({ error: 'Missing required fields' });86 return response.status(400).json({ error: 'Missing required fields' });
87 }87 }
8888
@@ -90,7 +90,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo
90 const user = await storage.getItem(toKey(request.body.handle));90 const user = await storage.getItem(toKey(request.body.handle));
9191
92 if (!user) {92 if (!user) {
93 console.log('Enable user failed: User not found');93 console.error('Enable user failed: User not found');
94 return response.status(404).json({ error: 'User not found' });94 return response.status(404).json({ error: 'User not found' });
95 }95 }
9696
@@ -106,7 +106,7 @@ router.post('/enable', requireAdminMiddleware, jsonParser, async (request, respo
106router.post('/promote', requireAdminMiddleware, jsonParser, async (request, response) => {106router.post('/promote', requireAdminMiddleware, jsonParser, async (request, response) => {
107 try {107 try {
108 if (!request.body.handle) {108 if (!request.body.handle) {
109 console.log('Promote user failed: Missing required fields');109 console.error('Promote user failed: Missing required fields');
110 return response.status(400).json({ error: 'Missing required fields' });110 return response.status(400).json({ error: 'Missing required fields' });
111 }111 }
112112
@@ -114,7 +114,7 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp
114 const user = await storage.getItem(toKey(request.body.handle));114 const user = await storage.getItem(toKey(request.body.handle));
115115
116 if (!user) {116 if (!user) {
117 console.log('Promote user failed: User not found');117 console.error('Promote user failed: User not found');
118 return response.status(404).json({ error: 'User not found' });118 return response.status(404).json({ error: 'User not found' });
119 }119 }
120120
@@ -130,12 +130,12 @@ router.post('/promote', requireAdminMiddleware, jsonParser, async (request, resp
130router.post('/demote', requireAdminMiddleware, jsonParser, async (request, response) => {130router.post('/demote', requireAdminMiddleware, jsonParser, async (request, response) => {
131 try {131 try {
132 if (!request.body.handle) {132 if (!request.body.handle) {
133 console.log('Demote user failed: Missing required fields');133 console.error('Demote user failed: Missing required fields');
134 return response.status(400).json({ error: 'Missing required fields' });134 return response.status(400).json({ error: 'Missing required fields' });
135 }135 }
136136
137 if (request.body.handle === request.user.profile.handle) {137 if (request.body.handle === request.user.profile.handle) {
138 console.log('Demote user failed: Cannot demote yourself');138 console.error('Demote user failed: Cannot demote yourself');
139 return response.status(400).json({ error: 'Cannot demote yourself' });139 return response.status(400).json({ error: 'Cannot demote yourself' });
140 }140 }
141141
@@ -143,7 +143,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo
143 const user = await storage.getItem(toKey(request.body.handle));143 const user = await storage.getItem(toKey(request.body.handle));
144144
145 if (!user) {145 if (!user) {
146 console.log('Demote user failed: User not found');146 console.error('Demote user failed: User not found');
147 return response.status(404).json({ error: 'User not found' });147 return response.status(404).json({ error: 'User not found' });
148 }148 }
149149
@@ -159,7 +159,7 @@ router.post('/demote', requireAdminMiddleware, jsonParser, async (request, respo
159router.post('/create', requireAdminMiddleware, jsonParser, async (request, response) => {159router.post('/create', requireAdminMiddleware, jsonParser, async (request, response) => {
160 try {160 try {
161 if (!request.body.handle || !request.body.name) {161 if (!request.body.handle || !request.body.name) {
162 console.log('Create user failed: Missing required fields');162 console.error('Create user failed: Missing required fields');
163 return response.status(400).json({ error: 'Missing required fields' });163 return response.status(400).json({ error: 'Missing required fields' });
164 }164 }
165165
@@ -167,12 +167,12 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
167 const handle = lodash.kebabCase(String(request.body.handle).toLowerCase().trim());167 const handle = lodash.kebabCase(String(request.body.handle).toLowerCase().trim());
168168
169 if (!handle) {169 if (!handle) {
170 console.log('Create user failed: Invalid handle');170 console.error('Create user failed: Invalid handle');
171 return response.status(400).json({ error: 'Invalid handle' });171 return response.status(400).json({ error: 'Invalid handle' });
172 }172 }
173173
174 if (handles.some(x => x === handle)) {174 if (handles.some(x => x === handle)) {
175 console.log('Create user failed: User with that handle already exists');175 console.error('Create user failed: User with that handle already exists');
176 return response.status(409).json({ error: 'User already exists' });176 return response.status(409).json({ error: 'User already exists' });
177 }177 }
178178
@@ -192,7 +192,7 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
192 await storage.setItem(toKey(handle), newUser);192 await storage.setItem(toKey(handle), newUser);
193193
194 // Create user directories194 // Create user directories
195 console.log('Creating data directories for', newUser.handle);195 console.info('Creating data directories for', newUser.handle);
196 await ensurePublicDirectoriesExist();196 await ensurePublicDirectoriesExist();
197 const directories = getUserDirectories(newUser.handle);197 const directories = getUserDirectories(newUser.handle);
198 await checkForNewContent([directories]);198 await checkForNewContent([directories]);
@@ -206,17 +206,17 @@ router.post('/create', requireAdminMiddleware, jsonParser, async (request, respo
206router.post('/delete', requireAdminMiddleware, jsonParser, async (request, response) => {206router.post('/delete', requireAdminMiddleware, jsonParser, async (request, response) => {
207 try {207 try {
208 if (!request.body.handle) {208 if (!request.body.handle) {
209 console.log('Delete user failed: Missing required fields');209 console.error('Delete user failed: Missing required fields');
210 return response.status(400).json({ error: 'Missing required fields' });210 return response.status(400).json({ error: 'Missing required fields' });
211 }211 }
212212
213 if (request.body.handle === request.user.profile.handle) {213 if (request.body.handle === request.user.profile.handle) {
214 console.log('Delete user failed: Cannot delete yourself');214 console.error('Delete user failed: Cannot delete yourself');
215 return response.status(400).json({ error: 'Cannot delete yourself' });215 return response.status(400).json({ error: 'Cannot delete yourself' });
216 }216 }
217217
218 if (request.body.handle === DEFAULT_USER.handle) {218 if (request.body.handle === DEFAULT_USER.handle) {
219 console.log('Delete user failed: Cannot delete default user');219 console.error('Delete user failed: Cannot delete default user');
220 return response.status(400).json({ error: 'Sorry, but the default user cannot be deleted. It is required as a fallback.' });220 return response.status(400).json({ error: 'Sorry, but the default user cannot be deleted. It is required as a fallback.' });
221 }221 }
222222
@@ -224,7 +224,7 @@ router.post('/delete', requireAdminMiddleware, jsonParser, async (request, respo
224224
225 if (request.body.purge) {225 if (request.body.purge) {
226 const directories = getUserDirectories(request.body.handle);226 const directories = getUserDirectories(request.body.handle);
227 console.log('Deleting data directories for', request.body.handle);227 console.info('Deleting data directories for', request.body.handle);
228 await fsPromises.rm(directories.root, { recursive: true, force: true });228 await fsPromises.rm(directories.root, { recursive: true, force: true });
229 }229 }
230230
@@ -238,7 +238,7 @@ router.post('/delete', requireAdminMiddleware, jsonParser, async (request, respo
238router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, response) => {238router.post('/slugify', requireAdminMiddleware, jsonParser, async (request, response) => {
239 try {239 try {
240 if (!request.body.text) {240 if (!request.body.text) {
241 console.log('Slugify failed: Missing required fields');241 console.error('Slugify failed: Missing required fields');
242 return response.status(400).json({ error: 'Missing required fields' });242 return response.status(400).json({ error: 'Missing required fields' });
243 }243 }
244244
src/endpoints/users-private.js+22 -22
@@ -57,18 +57,18 @@ router.get('/me', async (request, response) => {
57router.post('/change-avatar', jsonParser, async (request, response) => {57router.post('/change-avatar', jsonParser, async (request, response) => {
58 try {58 try {
59 if (!request.body.handle) {59 if (!request.body.handle) {
60 console.log('Change avatar failed: Missing required fields');60 console.error('Change avatar failed: Missing required fields');
61 return response.status(400).json({ error: 'Missing required fields' });61 return response.status(400).json({ error: 'Missing required fields' });
62 }62 }
6363
64 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {64 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
65 console.log('Change avatar failed: Unauthorized');65 console.error('Change avatar failed: Unauthorized');
66 return response.status(403).json({ error: 'Unauthorized' });66 return response.status(403).json({ error: 'Unauthorized' });
67 }67 }
6868
69 // Avatar is not a data URL or not an empty string69 // Avatar is not a data URL or not an empty string
70 if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {70 if (!request.body.avatar.startsWith('data:image/') && request.body.avatar !== '') {
71 console.log('Change avatar failed: Invalid data URL');71 console.error('Change avatar failed: Invalid data URL');
72 return response.status(400).json({ error: 'Invalid data URL' });72 return response.status(400).json({ error: 'Invalid data URL' });
73 }73 }
7474
@@ -76,7 +76,7 @@ router.post('/change-avatar', jsonParser, async (request, response) => {
76 const user = await storage.getItem(toKey(request.body.handle));76 const user = await storage.getItem(toKey(request.body.handle));
7777
78 if (!user) {78 if (!user) {
79 console.log('Change avatar failed: User not found');79 console.error('Change avatar failed: User not found');
80 return response.status(404).json({ error: 'User not found' });80 return response.status(404).json({ error: 'User not found' });
81 }81 }
8282
@@ -92,12 +92,12 @@ router.post('/change-avatar', jsonParser, async (request, response) => {
92router.post('/change-password', jsonParser, async (request, response) => {92router.post('/change-password', jsonParser, async (request, response) => {
93 try {93 try {
94 if (!request.body.handle) {94 if (!request.body.handle) {
95 console.log('Change password failed: Missing required fields');95 console.error('Change password failed: Missing required fields');
96 return response.status(400).json({ error: 'Missing required fields' });96 return response.status(400).json({ error: 'Missing required fields' });
97 }97 }
9898
99 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {99 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
100 console.log('Change password failed: Unauthorized');100 console.error('Change password failed: Unauthorized');
101 return response.status(403).json({ error: 'Unauthorized' });101 return response.status(403).json({ error: 'Unauthorized' });
102 }102 }
103103
@@ -105,17 +105,17 @@ router.post('/change-password', jsonParser, async (request, response) => {
105 const user = await storage.getItem(toKey(request.body.handle));105 const user = await storage.getItem(toKey(request.body.handle));
106106
107 if (!user) {107 if (!user) {
108 console.log('Change password failed: User not found');108 console.error('Change password failed: User not found');
109 return response.status(404).json({ error: 'User not found' });109 return response.status(404).json({ error: 'User not found' });
110 }110 }
111111
112 if (!user.enabled) {112 if (!user.enabled) {
113 console.log('Change password failed: User is disabled');113 console.error('Change password failed: User is disabled');
114 return response.status(403).json({ error: 'User is disabled' });114 return response.status(403).json({ error: 'User is disabled' });
115 }115 }
116116
117 if (!request.user.profile.admin && user.password && user.password !== getPasswordHash(request.body.oldPassword, user.salt)) {117 if (!request.user.profile.admin && user.password && user.password !== getPasswordHash(request.body.oldPassword, user.salt)) {
118 console.log('Change password failed: Incorrect password');118 console.error('Change password failed: Incorrect password');
119 return response.status(403).json({ error: 'Incorrect password' });119 return response.status(403).json({ error: 'Incorrect password' });
120 }120 }
121121
@@ -141,12 +141,12 @@ router.post('/backup', jsonParser, async (request, response) => {
141 const handle = request.body.handle;141 const handle = request.body.handle;
142142
143 if (!handle) {143 if (!handle) {
144 console.log('Backup failed: Missing required fields');144 console.error('Backup failed: Missing required fields');
145 return response.status(400).json({ error: 'Missing required fields' });145 return response.status(400).json({ error: 'Missing required fields' });
146 }146 }
147147
148 if (handle !== request.user.profile.handle && !request.user.profile.admin) {148 if (handle !== request.user.profile.handle && !request.user.profile.admin) {
149 console.log('Backup failed: Unauthorized');149 console.error('Backup failed: Unauthorized');
150 return response.status(403).json({ error: 'Unauthorized' });150 return response.status(403).json({ error: 'Unauthorized' });
151 }151 }
152152
@@ -162,7 +162,7 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
162 const password = request.body.password;162 const password = request.body.password;
163163
164 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(password, request.user.profile.salt)) {164 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(password, request.user.profile.salt)) {
165 console.log('Reset settings failed: Incorrect password');165 console.error('Reset settings failed: Incorrect password');
166 return response.status(403).json({ error: 'Incorrect password' });166 return response.status(403).json({ error: 'Incorrect password' });
167 }167 }
168168
@@ -180,12 +180,12 @@ router.post('/reset-settings', jsonParser, async (request, response) => {
180router.post('/change-name', jsonParser, async (request, response) => {180router.post('/change-name', jsonParser, async (request, response) => {
181 try {181 try {
182 if (!request.body.name || !request.body.handle) {182 if (!request.body.name || !request.body.handle) {
183 console.log('Change name failed: Missing required fields');183 console.error('Change name failed: Missing required fields');
184 return response.status(400).json({ error: 'Missing required fields' });184 return response.status(400).json({ error: 'Missing required fields' });
185 }185 }
186186
187 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {187 if (request.body.handle !== request.user.profile.handle && !request.user.profile.admin) {
188 console.log('Change name failed: Unauthorized');188 console.error('Change name failed: Unauthorized');
189 return response.status(403).json({ error: 'Unauthorized' });189 return response.status(403).json({ error: 'Unauthorized' });
190 }190 }
191191
@@ -193,7 +193,7 @@ router.post('/change-name', jsonParser, async (request, response) => {
193 const user = await storage.getItem(toKey(request.body.handle));193 const user = await storage.getItem(toKey(request.body.handle));
194194
195 if (!user) {195 if (!user) {
196 console.log('Change name failed: User not found');196 console.error('Change name failed: User not found');
197 return response.status(404).json({ error: 'User not found' });197 return response.status(404).json({ error: 'User not found' });
198 }198 }
199199
@@ -210,9 +210,9 @@ router.post('/change-name', jsonParser, async (request, response) => {
210router.post('/reset-step1', jsonParser, async (request, response) => {210router.post('/reset-step1', jsonParser, async (request, response) => {
211 try {211 try {
212 const resetCode = String(crypto.randomInt(1000, 9999));212 const resetCode = String(crypto.randomInt(1000, 9999));
213 console.log();213 console.info();
214 console.log(color.magenta(`${request.user.profile.name}, your account reset code is: `) + color.red(resetCode));214 console.info(color.magenta(`${request.user.profile.name}, your account reset code is: `) + color.red(resetCode));
215 console.log();215 console.info();
216 RESET_CACHE.set(request.user.profile.handle, resetCode);216 RESET_CACHE.set(request.user.profile.handle, resetCode);
217 return response.sendStatus(204);217 return response.sendStatus(204);
218 } catch (error) {218 } catch (error) {
@@ -224,23 +224,23 @@ router.post('/reset-step1', jsonParser, async (request, response) => {
224router.post('/reset-step2', jsonParser, async (request, response) => {224router.post('/reset-step2', jsonParser, async (request, response) => {
225 try {225 try {
226 if (!request.body.code) {226 if (!request.body.code) {
227 console.log('Recover step 2 failed: Missing required fields');227 console.error('Recover step 2 failed: Missing required fields');
228 return response.status(400).json({ error: 'Missing required fields' });228 return response.status(400).json({ error: 'Missing required fields' });
229 }229 }
230230
231 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(request.body.password, request.user.profile.salt)) {231 if (request.user.profile.password && request.user.profile.password !== getPasswordHash(request.body.password, request.user.profile.salt)) {
232 console.log('Recover step 2 failed: Incorrect password');232 console.error('Recover step 2 failed: Incorrect password');
233 return response.status(400).json({ error: 'Incorrect password' });233 return response.status(400).json({ error: 'Incorrect password' });
234 }234 }
235235
236 const code = RESET_CACHE.get(request.user.profile.handle);236 const code = RESET_CACHE.get(request.user.profile.handle);
237237
238 if (!code || code !== request.body.code) {238 if (!code || code !== request.body.code) {
239 console.log('Recover step 2 failed: Incorrect code');239 console.error('Recover step 2 failed: Incorrect code');
240 return response.status(400).json({ error: 'Incorrect code' });240 return response.status(400).json({ error: 'Incorrect code' });
241 }241 }
242242
243 console.log('Resetting account data:', request.user.profile.handle);243 console.info('Resetting account data:', request.user.profile.handle);
244 await fsPromises.rm(request.user.directories.root, { recursive: true, force: true });244 await fsPromises.rm(request.user.directories.root, { recursive: true, force: true });
245245
246 await ensurePublicDirectoriesExist();246 await ensurePublicDirectoriesExist();
src/endpoints/users-public.js+18 -18
@@ -56,7 +56,7 @@ router.post('/list', async (_request, response) => {
56router.post('/login', jsonParser, async (request, response) => {56router.post('/login', jsonParser, async (request, response) => {
57 try {57 try {
58 if (!request.body.handle) {58 if (!request.body.handle) {
59 console.log('Login failed: Missing required fields');59 console.error('Login failed: Missing required fields');
60 return response.status(400).json({ error: 'Missing required fields' });60 return response.status(400).json({ error: 'Missing required fields' });
61 }61 }
6262
@@ -67,17 +67,17 @@ router.post('/login', jsonParser, async (request, response) => {
67 const user = await storage.getItem(toKey(request.body.handle));67 const user = await storage.getItem(toKey(request.body.handle));
6868
69 if (!user) {69 if (!user) {
70 console.log('Login failed: User', request.body.handle, 'not found');70 console.error('Login failed: User', request.body.handle, 'not found');
71 return response.status(403).json({ error: 'Incorrect credentials' });71 return response.status(403).json({ error: 'Incorrect credentials' });
72 }72 }
7373
74 if (!user.enabled) {74 if (!user.enabled) {
75 console.log('Login failed: User', user.handle, 'is disabled');75 console.error('Login failed: User', user.handle, 'is disabled');
76 return response.status(403).json({ error: 'User is disabled' });76 return response.status(403).json({ error: 'User is disabled' });
77 }77 }
7878
79 if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {79 if (user.password && user.password !== getPasswordHash(request.body.password, user.salt)) {
80 console.log('Login failed: Incorrect password for', user.handle);80 console.error('Login failed: Incorrect password for', user.handle);
81 return response.status(403).json({ error: 'Incorrect credentials' });81 return response.status(403).json({ error: 'Incorrect credentials' });
82 }82 }
8383
@@ -88,11 +88,11 @@ router.post('/login', jsonParser, async (request, response) => {
8888
89 await loginLimiter.delete(ip);89 await loginLimiter.delete(ip);
90 request.session.handle = user.handle;90 request.session.handle = user.handle;
91 console.log('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());91 console.info('Login successful:', user.handle, 'from', ip, 'at', new Date().toLocaleString());
92 return response.json({ handle: user.handle });92 return response.json({ handle: user.handle });
93 } catch (error) {93 } catch (error) {
94 if (error instanceof RateLimiterRes) {94 if (error instanceof RateLimiterRes) {
95 console.log('Login failed: Rate limited from', getIpFromRequest(request));95 console.error('Login failed: Rate limited from', getIpFromRequest(request));
96 return response.status(429).send({ error: 'Too many attempts. Try again later or recover your password.' });96 return response.status(429).send({ error: 'Too many attempts. Try again later or recover your password.' });
97 }97 }
9898
@@ -104,7 +104,7 @@ router.post('/login', jsonParser, async (request, response) => {
104router.post('/recover-step1', jsonParser, async (request, response) => {104router.post('/recover-step1', jsonParser, async (request, response) => {
105 try {105 try {
106 if (!request.body.handle) {106 if (!request.body.handle) {
107 console.log('Recover step 1 failed: Missing required fields');107 console.error('Recover step 1 failed: Missing required fields');
108 return response.status(400).json({ error: 'Missing required fields' });108 return response.status(400).json({ error: 'Missing required fields' });
109 }109 }
110110
@@ -115,24 +115,24 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
115 const user = await storage.getItem(toKey(request.body.handle));115 const user = await storage.getItem(toKey(request.body.handle));
116116
117 if (!user) {117 if (!user) {
118 console.log('Recover step 1 failed: User', request.body.handle, 'not found');118 console.error('Recover step 1 failed: User', request.body.handle, 'not found');
119 return response.status(404).json({ error: 'User not found' });119 return response.status(404).json({ error: 'User not found' });
120 }120 }
121121
122 if (!user.enabled) {122 if (!user.enabled) {
123 console.log('Recover step 1 failed: User', user.handle, 'is disabled');123 console.error('Recover step 1 failed: User', user.handle, 'is disabled');
124 return response.status(403).json({ error: 'User is disabled' });124 return response.status(403).json({ error: 'User is disabled' });
125 }125 }
126126
127 const mfaCode = String(crypto.randomInt(1000, 9999));127 const mfaCode = String(crypto.randomInt(1000, 9999));
128 console.log();128 console.info();
129 console.log(color.blue(`${user.name}, your password recovery code is: `) + color.magenta(mfaCode));129 console.info(color.blue(`${user.name}, your password recovery code is: `) + color.magenta(mfaCode));
130 console.log();130 console.info();
131 MFA_CACHE.set(user.handle, mfaCode);131 MFA_CACHE.set(user.handle, mfaCode);
132 return response.sendStatus(204);132 return response.sendStatus(204);
133 } catch (error) {133 } catch (error) {
134 if (error instanceof RateLimiterRes) {134 if (error instanceof RateLimiterRes) {
135 console.log('Recover step 1 failed: Rate limited from', getIpFromRequest(request));135 console.error('Recover step 1 failed: Rate limited from', getIpFromRequest(request));
136 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });136 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });
137 }137 }
138138
@@ -144,7 +144,7 @@ router.post('/recover-step1', jsonParser, async (request, response) => {
144router.post('/recover-step2', jsonParser, async (request, response) => {144router.post('/recover-step2', jsonParser, async (request, response) => {
145 try {145 try {
146 if (!request.body.handle || !request.body.code) {146 if (!request.body.handle || !request.body.code) {
147 console.log('Recover step 2 failed: Missing required fields');147 console.error('Recover step 2 failed: Missing required fields');
148 return response.status(400).json({ error: 'Missing required fields' });148 return response.status(400).json({ error: 'Missing required fields' });
149 }149 }
150150
@@ -153,12 +153,12 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
153 const ip = getIpFromRequest(request);153 const ip = getIpFromRequest(request);
154154
155 if (!user) {155 if (!user) {
156 console.log('Recover step 2 failed: User', request.body.handle, 'not found');156 console.error('Recover step 2 failed: User', request.body.handle, 'not found');
157 return response.status(404).json({ error: 'User not found' });157 return response.status(404).json({ error: 'User not found' });
158 }158 }
159159
160 if (!user.enabled) {160 if (!user.enabled) {
161 console.log('Recover step 2 failed: User', user.handle, 'is disabled');161 console.error('Recover step 2 failed: User', user.handle, 'is disabled');
162 return response.status(403).json({ error: 'User is disabled' });162 return response.status(403).json({ error: 'User is disabled' });
163 }163 }
164164
@@ -166,7 +166,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
166166
167 if (request.body.code !== mfaCode) {167 if (request.body.code !== mfaCode) {
168 await recoverLimiter.consume(ip);168 await recoverLimiter.consume(ip);
169 console.log('Recover step 2 failed: Incorrect code');169 console.error('Recover step 2 failed: Incorrect code');
170 return response.status(403).json({ error: 'Incorrect code' });170 return response.status(403).json({ error: 'Incorrect code' });
171 }171 }
172172
@@ -186,7 +186,7 @@ router.post('/recover-step2', jsonParser, async (request, response) => {
186 return response.sendStatus(204);186 return response.sendStatus(204);
187 } catch (error) {187 } catch (error) {
188 if (error instanceof RateLimiterRes) {188 if (error instanceof RateLimiterRes) {
189 console.log('Recover step 2 failed: Rate limited from', getIpFromRequest(request));189 console.error('Recover step 2 failed: Rate limited from', getIpFromRequest(request));
190 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });190 return response.status(429).send({ error: 'Too many attempts. Try again later or contact your admin.' });
191 }191 }
192192
src/endpoints/vectors.js+3 -3
@@ -360,7 +360,7 @@ async function regenerateCorruptedIndexErrorHandler(req, res, error) {
360360
361 if (exists) {361 if (exists) {
362 const path = index.folderPath;362 const path = index.folderPath;
363 console.error(`Corrupted index detected at ${path}, regenerating...`);363 console.warn(`Corrupted index detected at ${path}, regenerating...`);
364 await index.deleteIndex();364 await index.deleteIndex();
365 return res.redirect(307, req.originalUrl + '?regenerated=true');365 return res.redirect(307, req.originalUrl + '?regenerated=true');
366 }366 }
@@ -474,7 +474,7 @@ router.post('/purge-all', jsonParser, async (req, res) => {
474 continue;474 continue;
475 }475 }
476 await fs.promises.rm(sourcePath, { recursive: true });476 await fs.promises.rm(sourcePath, { recursive: true });
477 console.log(`Deleted vector source store at ${sourcePath}`);477 console.info(`Deleted vector source store at ${sourcePath}`);
478 }478 }
479479
480 return res.sendStatus(200);480 return res.sendStatus(200);
@@ -498,7 +498,7 @@ router.post('/purge', jsonParser, async (req, res) => {
498 continue;498 continue;
499 }499 }
500 await fs.promises.rm(sourcePath, { recursive: true });500 await fs.promises.rm(sourcePath, { recursive: true });
501 console.log(`Deleted vector index at ${sourcePath}`);501 console.info(`Deleted vector index at ${sourcePath}`);
502 }502 }
503503
504 return res.sendStatus(200);504 return res.sendStatus(200);
src/endpoints/worldinfo.js+1 -1
@@ -25,7 +25,7 @@ export function readWorldInfoFile(directories, worldInfoName, allowDummy) {
25 const pathToWorldInfo = path.join(directories.worlds, filename);25 const pathToWorldInfo = path.join(directories.worlds, filename);
2626
27 if (!fs.existsSync(pathToWorldInfo)) {27 if (!fs.existsSync(pathToWorldInfo)) {
28 console.log(`World info file ${filename} doesn't exist.`);28 console.error(`World info file ${filename} doesn't exist.`);
29 return dummyObject;29 return dummyObject;
30 }30 }
3131
src/request-proxy.js+3 -3
@@ -43,9 +43,9 @@ export default function initRequestProxy({ enabled, url, bypass }) {
43 http.globalAgent = proxyAgent;43 http.globalAgent = proxyAgent;
44 https.globalAgent = proxyAgent;44 https.globalAgent = proxyAgent;
4545
46 console.log();46 console.info();
47 console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));47 console.info(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url));
48 console.log();48 console.info();
49 } catch (error) {49 } catch (error) {
50 console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);50 console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error);
51 }51 }
src/util.js+21 -3
@@ -13,6 +13,7 @@ import _ from 'lodash';
13import yauzl from 'yauzl';13import yauzl from 'yauzl';
14import mime from 'mime-types';14import mime from 'mime-types';
15import { default as simpleGit } from 'simple-git';15import { default as simpleGit } from 'simple-git';
16import { LOG_LEVELS } from './constants.js';
1617
17/**18/**
18 * Parsed config object.19 * Parsed config object.
@@ -149,6 +150,7 @@ export function getHexString(length) {
149 */150 */
150export function formatBytes(bytes) {151export function formatBytes(bytes) {
151 if (bytes === 0) return '0 B';152 if (bytes === 0) return '0 B';
153
152 const k = 1024;154 const k = 1024;
153 const sizes = ['B', 'KB', 'MB', 'GB'];155 const sizes = ['B', 'KB', 'MB', 'GB'];
154 const i = Math.floor(Math.log(bytes) / Math.log(k));156 const i = Math.floor(Math.log(bytes) / Math.log(k));
@@ -285,10 +287,11 @@ export function deepMerge(target, source) {
285 if (isObject(target) && isObject(source)) {287 if (isObject(target) && isObject(source)) {
286 Object.keys(source).forEach(key => {288 Object.keys(source).forEach(key => {
287 if (isObject(source[key])) {289 if (isObject(source[key])) {
288 if (!(key in target))290 if (!(key in target)) {
289 Object.assign(output, { [key]: source[key] });291 Object.assign(output, { [key]: source[key] });
290 else292 } else {
291 output[key] = deepMerge(target[key], source[key]);293 output[key] = deepMerge(target[key], source[key]);
294 }
292 } else {295 } else {
293 Object.assign(output, { [key]: source[key] });296 Object.assign(output, { [key]: source[key] });
294 }297 }
@@ -466,7 +469,9 @@ export function forwardFetchResponse(from, to) {
466 from.body.pipe(to);469 from.body.pipe(to);
467470
468 to.socket.on('close', function () {471 to.socket.on('close', function () {
469 if (from.body instanceof Readable) from.body.destroy(); // Close the remote stream472 if (from.body instanceof Readable) {
473 from.body.destroy(); // Close the remote stream
474 }
470 to.end(); // End the Express response475 to.end(); // End the Express response
471 });476 });
472477
@@ -693,6 +698,19 @@ export function isValidUrl(url) {
693}698}
694699
695/**700/**
701 * Setup the minimum log level
702 */
703export function setupLogLevel() {
704 const logLevel = getConfigValue('minLogLevel', LOG_LEVELS.DEBUG);
705
706 globalThis.console.debug = logLevel <= LOG_LEVELS.DEBUG ? console.debug : () => {};
707 globalThis.console.log = logLevel <= LOG_LEVELS.INFO ? console.log : () => {};
708 globalThis.console.info = logLevel <= LOG_LEVELS.INFO ? console.info : () => {};
709 globalThis.console.warn = logLevel <= LOG_LEVELS.WARN ? console.warn : () => {};
710 globalThis.console.error = logLevel <= LOG_LEVELS.ERROR ? console.error : () => {};
711}
712
713/**
696 * MemoryLimitedMap class that limits the memory usage of string values.714 * MemoryLimitedMap class that limits the memory usage of string values.
697 */715 */
698export class MemoryLimitedMap {716export class MemoryLimitedMap {