What is hapta? - hapta is a backend websocket server layer for pocketbase, it helps you secure connections to your pocketbase database, aswell as allows ratelimit spam protection and total controll over every database crud method.
Why does postr use this? - Postr requires 10's of thousands of connections at once - we would not want all those requests to be served to the database directly. Instead we can authenticate - ratelimit - cache - then serve the content from the database which provides a smoother experience.
Stop If you do not know how to use pocketbase
Download a release
In your env file you can edit the following needed fields
DB_URL=
ADMIN_EMAIL=
ADMIN_PASSWORD=
HAPTA_ADMIN_KEY=
SSL_ENABLED=true
JWT_SECRET=
Create a config.ts file and paste the following
export default {
port: 3000,
hostname: "localhost",
developmentMode: true,
ratelimits:{
default:{
limit: 10,
every: 1000,
maxUses: 0
},
list:{
limit: 10,
every: 1000,
maxUses: 0
},
},
rules: '/rules.ts',
}
You can write a rules entry file to validate all records before and or after they are returned to db/user
console.log('worker started');
declare var self: Worker;
declare var TokenManager: globalThis.TokenManager;
declare var ErrorCodes: globalThis.ErrorCodes
self.onmessage = (event: MessageEvent) => {
const { type, data, id, token } = event.data.record;
const checkOwnership = () => {
const authID = TokenManager.decode(token).id;
if (authID !== id) {
self.postMessage({ error: true, code: ErrorCodes.OWNERSHIP_REQUIRED });
return false;
}
return true;
};
switch (type) {
case 'update':
try {
if (!checkOwnership()) return;
if (data.collection === "users" && TokenManager.decode(token).id == id) {
const cannotUpdate = ['validVerified', 'postr_plus', 'followers', 'postr_subscriber_since'];
for (const key in data.record) {
if (cannotUpdate.includes(key)) {
self.postMessage({ error: true, code: ErrorCodes.OWNERSHIP_REQUIRED });
return;
}
}
}else{
const others = ['username', 'email', 'verified', 'validVerified', 'postr_plus', 'following', 'bio', 'postr_subscriber_since'];
for (const key in data.record) {
if (!others.includes(key)) {
self.postMessage({ error: true, code: ErrorCodes.OWNERSHIP_REQUIRED });
return;
}
}
}
self.postMessage({ error: false, message: 'success' });
} catch (error) {
console.log(error);
}
break;
case 'delete':
if (!checkTokenValidity() || !checkOwnership()) return;
self.postMessage({ error: false, message: 'success' });
break;
}
};
chmod +x ./hapta-server && ./hapta-server
./build.sh
and your code will be packaged to a hapta-server file for several server types