Select Git revision
handlePocketBase.js
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
handlePocketBase.js 9.63 KiB
import { CustomError } from "../handleErrors.js";
import PocketBase, { getTokenPayload } from 'pocketbase';
import { mapChatMessagesToStoredMessages, mapStoredMessagesToChatMessages } from "@langchain/core/messages";
export const pb = new PocketBase(process.env.PB_API_URL);
/**
* CREATE POCKETBASE SUPERADMIN
* if we cannot login with the admin user, we create it
* creating will only work on the first run
*/
export const createPBSuperAdmin = async () => {
let admin = {};
try {
// login admin user
// console.log('PB: Checking for admin user...');
// try to login - just to check if admin user exists
admin = await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
// logout
pb.authStore.clear();
} catch (error) {
try {
// try to create admin user - only works on first run
// console.log('PB: Creating admin user...');
admin = await pb.admins.create({
"email": process.env['PB_ADMIN_EMAIL'],
"password": process.env['PB_ADMIN_PASSWORD'],
"confirmPassword": process.env['PB_ADMIN_PASSWORD']
});
} catch (error) {
// exit if neither login nor create worked
console.error(error);
return;
}
}
return admin;
};
/**
* GET SETTINGS
* fetch & return settings from running instance
*/
export const getPBSettings = async () => {
await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
const settings = await pb.settings.getAll();
pb.authStore.clear();
return settings;
};
/**
* UPDATE SETTINGS
* update settings on running instance & return updated settings
* TODO add custom field users:role
* TODO edit email templates
*/
export const updatePBSettings = async () => {
// convert string to boolean
const tls = process.env['SMTP_SECURE'].toLowerCase() === 'true' ? true : false;
// login as admin
await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
// update settings
const settings = await pb.settings.update({
meta: {
"appName": process.env.npm_package_name,
appUrl: process.env['PB_API_URL'],
senderName: process.env['SMTP_FROM_NAME'],
senderAddress: process.env['SMTP_FROM_ADDRESS']
},
recordAuthToken: {
secret: process.env['PB_JWT_SECRET'],
duration: Number(process.env['PB_JWT_EXPIRES_IN'])
},
smtp: {
enabled: true,
host: process.env['SMTP_HOST'],
port: Number(process.env['SMTP_PORT']),
username: process.env['SMTP_USER'],
password: process.env['SMTP_PASSWORD'],
authMethod: "LOGIN",
tls: false
}
});
// send test mail to admin
// await pb.settings.testEmail(process.env['PB_ADMIN_EMAIL'], "verification");
// logout
pb.authStore.clear();
return settings;
};
/**
* GET COLLECTIONS
*/
export const getPBCollections = async (req, res, next) => {
// login as admin
await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
// get collections
const collections = await pb.collections.getFullList();
// logout
pb.authStore.clear();
return collections;
};
/**
* CREATE COLLECTION
* for structure of collection see https://pocketbase.io/docs/api-collections/#create-collection
*/
export const createPBCollection = async (collection) => {
try {
// CHECK IF COLLECTION EXITSTS
// fetch all collections
const collections = await getPBCollections();
// check if collection already exists
if (collections.some(e => e.name === collection.name)) {
// console.log('Collection already exists:', collection.name);
// fetch existing collection
await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
const response = await pb.collections.getOne(collection.name);
// logout
pb.authStore.clear();
// return existing collection
return response;
}
// CREATE COLLECTION
// login as admin
await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
// create collection
const response = await pb.collections.create(collection);
// logout
pb.authStore.clear();
// const chatCollection = await pb.collections.getOne('chat');
// console.log(JSON.stringify(chat));
return response;
} catch (error) {
console.error(error);
return;
}
};
/**
* GET CHAT RECORD
*/
export const getChatRecordByID = async (res, id) => {
try {
return await pb.collection('chats').getFirstListItem(`id="${id}"`);
} catch (error) {
if (error.status === 404) throw { status: 404, message: `No chat history with ID ${id} found.` };
next(error);
}
};
/**
* GET ALL USERS CHAT RECORDS
*/
export const pbGetChatsByUserId = async (res) => {
try {
const chats = await pb.collection('chats').getFullList({
filter: `user="${pb.authStore.model.id}"`,
sort: '-created',
});
return chats;
} catch (error) {
next(error);
}
};
/**
* CREATE CHAT
*/
export const pbCreateChat = async (res, title) => {
try {
return await pb.collection('chats').create({ title, user: pb.authStore.model.id });
} catch (error) {
next(error);
}
};
// TODO write as middleware? But maybe that'll swallow streaming the response
export const extendChat = async (res, chatId, messages) => {
// exit if no chatId
if (!chatId) {
console.error('No chatId provided');
return;
}
try {
// fetch chat record
let record = await pb.collection('chats').getFirstListItem(`id="${chatId}"`);
// push new message into chat history
const serializedMessages = mapChatMessagesToStoredMessages(messages);
record.chatHistory ? record.chatHistory.push(...serializedMessages) : record.chatHistory = serializedMessages;
// save chat
record = await pb.collection('chats').update(
chatId,
{
chatHistory: record.chatHistory
}
);
// return updated chat
return record;
} catch (error) {
console.error('extendChat error: ', error);
next(error);
}
};
/**
* VERIFY TOKEN
* check if JWT is valid
*/
export const pbVerifyAccessToken = async (req, res, next) => {
// check if token is valid
if (!pb.authStore.isValid) {
return res.status(403).json({ message: 'You are not logged in.' });
}
// remember if token is admin
const payload = getTokenPayload(pb.authStore.token);
pb.authStore.model.isAdmin = (payload.type === "admin" ? true : false);
// continue
next();
};
/**
* CREATE RECORD IN PB
*/
export const pbCreateRecord = async (res, collection, record) => {
try {
// create record
return await pb.collection(collection).create(record);
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* SEND PB's VERIFICATION EMAIL
*/
export const pbRequestVerification = async (res, collection, email) => {
try {
// request verification
return await pb.collection(collection).requestVerification(email);
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* LOGIN AS PB ADMIN
*/
export const pbAdminLogin = async (res) => {
try {
// login admin
const admin = await pb.admins.authWithPassword(process.env['PB_ADMIN_EMAIL'], process.env['PB_ADMIN_PASSWORD']);
return admin;
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* GET FIRST USER ENTRY
*/
export const pbGetUser = async (email) => {
try {
// search for user
return await pb.collection("users").getFirstListItem(`email="${email}"`);
} catch (error) {
error.name = 'PBError';
return error;
}
};
/**
* VALIDATE VERIFICATION TOKEN
*/
export const pbValidateVerificationToken = async (res, token) => {
try {
// validate token
await pb.collection("users").confirmVerification(token);
return true;
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* LOGIN AS PB USER
*/
export const pbUserLogin = async (res, email, password) => {
try {
// login
return await pb.collection('users').authWithPassword(email, password);
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* REFRESH JWT
*/
export const pbRefreshJWT = async (req, res) => {
try {
return await pb.collection('users').authRefresh();
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* CLEAR AUTH STORE
*/
export const pbClearAuthStore = async () => {
try {
return pb.authStore.clear();
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* REQUEST PASSWORD RESET
*/
export const pbRequestPasswordReset = async (res, email) => {
try {
// request password reset
return await pb.collection('users').requestPasswordReset(email);
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* CONFIRM PASSWORD RESET
*/
export const pbConfirmPasswordReset = async (res, token, password, confirmPassword) => {
try {
// confirm password reset
return await pb.collection('users').confirmPasswordReset(token, password, confirmPassword);
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* REQUEST EMAIL CHANGE
*/
export const pbRequestEmailChange = async (res, email) => {
try {
// request email change
const result = await pb.collection('users').requestEmailChange(email);
return result;
} catch (error) {
error.name = 'PBError';
next(error);
}
};
/**
* CONFIRM EMAIL CHANGE
*/
export const pbConfirmEmailChange = async (res, token, password) => {
try {
// confirm email change
return await pb.collection('users').confirmEmailChange(token, password);
} catch (error) {
error.name = 'PBError';
next(error);
}
};