resolve issues with new features
This commit is contained in:
@@ -98,7 +98,7 @@ async function getRecentPages(): Promise<BookStackPage[]> {
|
||||
const pages: BookStackPage[] = response.data?.data ?? [];
|
||||
return pages.map((p) => ({
|
||||
...p,
|
||||
url: `${bookstack.url}/books/${p.book_slug}/page/${p.slug}`,
|
||||
url: p.url && p.url.startsWith('http') ? p.url : `${bookstack.url}/books/${p.book_slug}/page/${p.slug}`,
|
||||
}));
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
@@ -122,7 +122,7 @@ async function searchPages(query: string): Promise<BookStackSearchResult[]> {
|
||||
const response = await axios.get(
|
||||
`${bookstack.url}/api/search`,
|
||||
{
|
||||
params: { query, count: 8 },
|
||||
params: { query, count: 50 },
|
||||
headers: buildHeaders(),
|
||||
},
|
||||
);
|
||||
@@ -189,7 +189,7 @@ async function getPageById(id: number): Promise<BookStackPageDetail> {
|
||||
html: page.html ?? '',
|
||||
created_at: page.created_at,
|
||||
updated_at: page.updated_at,
|
||||
url: `${bookstack.url}/books/${page.book_slug}/page/${page.slug}`,
|
||||
url: page.url && page.url.startsWith('http') ? page.url : `${bookstack.url}/books/${page.book_slug}/page/${page.slug}`,
|
||||
book: page.book,
|
||||
createdBy: page.created_by,
|
||||
updatedBy: page.updated_by,
|
||||
|
||||
@@ -200,30 +200,47 @@ async function getMessages(token: string, loginName: string, appPassword: string
|
||||
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
|
||||
}
|
||||
|
||||
const response = await axios.get(
|
||||
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/chat/${encodeURIComponent(token)}`,
|
||||
{
|
||||
params: { lookIntoFuture: 0, limit: 50, setReadMarker: 0 },
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
|
||||
'OCS-APIRequest': 'true',
|
||||
'Accept': 'application/json',
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/chat/${encodeURIComponent(token)}`,
|
||||
{
|
||||
params: { lookIntoFuture: 0, limit: 50, setReadMarker: 0 },
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
|
||||
'OCS-APIRequest': 'true',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
);
|
||||
|
||||
const messages: any[] = response.data?.ocs?.data ?? [];
|
||||
return messages.map((m: any) => ({
|
||||
id: m.id,
|
||||
token: m.token,
|
||||
actorType: m.actorType,
|
||||
actorId: m.actorId,
|
||||
actorDisplayName: m.actorDisplayName,
|
||||
message: m.message,
|
||||
timestamp: m.timestamp,
|
||||
messageType: m.messageType ?? '',
|
||||
systemMessage: m.systemMessage ?? '',
|
||||
}));
|
||||
const messages: any[] = response.data?.ocs?.data ?? [];
|
||||
return messages.map((m: any) => ({
|
||||
id: m.id,
|
||||
token: m.token,
|
||||
actorType: m.actorType,
|
||||
actorId: m.actorId,
|
||||
actorDisplayName: m.actorDisplayName,
|
||||
message: m.message,
|
||||
timestamp: m.timestamp,
|
||||
messageType: m.messageType ?? '',
|
||||
systemMessage: m.systemMessage ?? '',
|
||||
}));
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
const err = new Error('Nextcloud authentication invalid');
|
||||
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
|
||||
throw err;
|
||||
}
|
||||
if (axios.isAxiosError(error)) {
|
||||
logger.error('NextcloudService.getMessages failed', {
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
});
|
||||
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
|
||||
}
|
||||
logger.error('NextcloudService.getMessages failed', { error });
|
||||
throw new Error('Failed to fetch messages');
|
||||
}
|
||||
}
|
||||
|
||||
async function sendMessage(token: string, message: string, loginName: string, appPassword: string): Promise<void> {
|
||||
@@ -232,18 +249,35 @@ async function sendMessage(token: string, message: string, loginName: string, ap
|
||||
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
|
||||
}
|
||||
|
||||
await axios.post(
|
||||
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/chat/${encodeURIComponent(token)}`,
|
||||
{ message },
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
|
||||
'OCS-APIRequest': 'true',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
try {
|
||||
await axios.post(
|
||||
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/chat/${encodeURIComponent(token)}`,
|
||||
{ message },
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
|
||||
'OCS-APIRequest': 'true',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
);
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
const err = new Error('Nextcloud authentication invalid');
|
||||
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
|
||||
throw err;
|
||||
}
|
||||
if (axios.isAxiosError(error)) {
|
||||
logger.error('NextcloudService.sendMessage failed', {
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
});
|
||||
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
|
||||
}
|
||||
logger.error('NextcloudService.sendMessage failed', { error });
|
||||
throw new Error('Failed to send message');
|
||||
}
|
||||
}
|
||||
|
||||
async function markAsRead(token: string, loginName: string, appPassword: string): Promise<void> {
|
||||
@@ -252,16 +286,33 @@ async function markAsRead(token: string, loginName: string, appPassword: string)
|
||||
throw new Error('NEXTCLOUD_URL is not configured or is not a valid service URL');
|
||||
}
|
||||
|
||||
await axios.delete(
|
||||
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/chat/${encodeURIComponent(token)}/read`,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
|
||||
'OCS-APIRequest': 'true',
|
||||
'Accept': 'application/json',
|
||||
try {
|
||||
await axios.delete(
|
||||
`${baseUrl}/ocs/v2.php/apps/spreed/api/v4/chat/${encodeURIComponent(token)}/read`,
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Basic ${Buffer.from(loginName + ':' + appPassword).toString('base64')}`,
|
||||
'OCS-APIRequest': 'true',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
);
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401) {
|
||||
const err = new Error('Nextcloud authentication invalid');
|
||||
(err as any).code = 'NEXTCLOUD_AUTH_INVALID';
|
||||
throw err;
|
||||
}
|
||||
if (axios.isAxiosError(error)) {
|
||||
logger.error('NextcloudService.markAsRead failed', {
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
});
|
||||
throw new Error(`Nextcloud API error: ${error.response?.status ?? 'unknown'}`);
|
||||
}
|
||||
logger.error('NextcloudService.markAsRead failed', { error });
|
||||
throw new Error('Failed to mark conversation as read');
|
||||
}
|
||||
}
|
||||
|
||||
async function getConversations(loginName: string, appPassword: string): Promise<ConversationsResult> {
|
||||
|
||||
43
backend/src/services/settings.service.ts
Normal file
43
backend/src/services/settings.service.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import pool from '../config/database';
|
||||
|
||||
export interface AppSetting {
|
||||
key: string;
|
||||
value: any;
|
||||
updated_at: string;
|
||||
updated_by: string | null;
|
||||
}
|
||||
|
||||
class SettingsService {
|
||||
async getAll(): Promise<AppSetting[]> {
|
||||
const result = await pool.query('SELECT * FROM app_settings ORDER BY key');
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
async get(key: string): Promise<AppSetting | null> {
|
||||
const result = await pool.query('SELECT * FROM app_settings WHERE key = $1', [key]);
|
||||
return result.rows[0] ?? null;
|
||||
}
|
||||
|
||||
async set(key: string, value: any, userId: string): Promise<AppSetting> {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO app_settings (key, value, updated_by, updated_at)
|
||||
VALUES ($1, $2, $3, NOW())
|
||||
ON CONFLICT (key) DO UPDATE SET value = $2, updated_by = $3, updated_at = NOW()
|
||||
RETURNING *`,
|
||||
[key, JSON.stringify(value), userId]
|
||||
);
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
async delete(key: string): Promise<boolean> {
|
||||
const result = await pool.query('DELETE FROM app_settings WHERE key = $1', [key]);
|
||||
return (result.rowCount ?? 0) > 0;
|
||||
}
|
||||
|
||||
async getExternalLinks(): Promise<Array<{name: string; url: string}>> {
|
||||
const setting = await this.get('external_links');
|
||||
return Array.isArray(setting?.value) ? setting.value : [];
|
||||
}
|
||||
}
|
||||
|
||||
export default new SettingsService();
|
||||
Reference in New Issue
Block a user