first commit

This commit is contained in:
Kevand
2026-02-04 00:21:37 +00:00
commit 58925b1ac0
18 changed files with 1086 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
pocketbase
pb_data

23
Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM alpine:latest
ARG PB_VERSION=0.36.2
RUN apk add --no-cache \
unzip \
ca-certificates
# download and unzip PocketBase
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
RUN unzip /tmp/pb.zip -d /pb/
RUN mkdir /pb/pb_public
# uncomment to copy the local pb_migrations dir into the image
COPY ./pb_migrations /pb/pb_migrations
# uncomment to copy the local pb_hooks dir into the image
COPY ./pb_hooks /pb/pb_hooks
EXPOSE 8080
# start PocketBase
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8080"]

113
pb_hooks/main.pb.js Normal file
View File

@@ -0,0 +1,113 @@
/// <reference path="../pb_data/types.d.ts" />
onBootstrap((e) => {
e.next();
console.log("App initialized!")
})
onRecordCreate((e) => {
if (!e.record.get("token")) {
e.record.set('token', $security.randomString(32));
}
e.next();
}, 'invites')
onRecordCreate((e) => {
const token = e.record.get('invite_token');
const email = e.record.email();
if (!token) {
throw new BadRequestError('Registration requires a valid invite token');
}
try {
const invite = $app.findFirstRecordByData('invites', 'token', token);
if (invite.email() !== email) {
throw new BadRequestError('This invite is for a different email');
}
const createdUnix = invite.getDateTime('created').unix();
const nowUnix = new Date().getTime() / 1000;
const oneDaySeconds = 86400;
if ((nowUnix - createdUnix) > oneDaySeconds) {
throw new BadRequestError('This invite has expired');
}
} catch (err) {
if (err instanceof BadRequestError) {
throw err;
}
throw new BadRequestError('Invalid invite token');
}
e.next();
}, 'users')
onRecordAfterCreateSuccess((e) => {
const token = e.record.get('invite_token');
if (token) {
try {
const invite = $app.findFirstRecordByData('invites', 'token', token)
$app.delete(invite);
const user = e.record;
user.set('invite_token', '');
user.set('verified', true)
$app.save(user);
} catch (err) {
console.warn('Could not delete used invite:', err)
}
}
e.next();
}, 'users')
onRecordAfterCreateSuccess((e) => {
const email = e.record.email();
const token = e.record.get('token');
const appUrl = e.app.settings().meta.appURL;
const inviteLink = `${appUrl}/accept-invite?token=${token}`
const message = new MailerMessage({
from: {
address: e.app.settings().meta.senderAddress,
name: e.app.settings().meta.senderName
},
to: [{ address: email }],
subject: "Zapraszam przyjacielu",
html: `
<h1>Zapraszam serdecznie</h1>
<p>Naciśnij w linka by założyć konto (link wygaśnie za 24 godziny)</p>
<p><a href="${inviteLink}">${inviteLink}</a></p>
`
})
try {
e.app.newMailClient().send(message)
} catch (e) {
console.error('Failed to send invite email:', e);
}
e.next();
}, 'invites');
cronAdd("cleanup_invites", "0 0 * * *", () => {
console.log("Cleaning up invites...");
try {
$app.db().newQuery("DELETE FROM invites WHERE created < datetime('now', '-1 day')").execute()
} catch (err) {
console.error("Clean up failed:", err)
}
})

13
pb_hooks/util.js Normal file
View File

@@ -0,0 +1,13 @@
module.exports = {
uuidv4() {
const uuid = new Array(36);
for (let i = 0; i < 36; i++) {
uuid[i] = Math.floor(Math.random() * 16)
}
uuid[14] = 4;
uuid[19] = uuid[19] &= ~(1 << 2);
uuid[19] = uuid[19] |= (1 << 3);
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
return uuid.map(x => x.toString(16)).join('')
}
}

View File

@@ -0,0 +1,88 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = new Collection({
"createRule": null,
"deleteRule": null,
"fields": [
{
"autogeneratePattern": "[a-z0-9]{15}",
"hidden": false,
"id": "text3208210256",
"max": 15,
"min": 15,
"name": "id",
"pattern": "^[a-z0-9]+$",
"presentable": false,
"primaryKey": true,
"required": true,
"system": true,
"type": "text"
},
{
"exceptDomains": null,
"hidden": false,
"id": "email1181691900",
"name": "target",
"onlyDomains": null,
"presentable": false,
"required": false,
"system": false,
"type": "email"
},
{
"hidden": false,
"id": "bool2034721560",
"name": "used",
"presentable": false,
"required": false,
"system": false,
"type": "bool"
},
{
"hidden": false,
"id": "date2593941644",
"max": "",
"min": "",
"name": "expires",
"presentable": false,
"required": false,
"system": false,
"type": "date"
},
{
"hidden": false,
"id": "autodate2990389176",
"name": "created",
"onCreate": true,
"onUpdate": false,
"presentable": false,
"system": false,
"type": "autodate"
},
{
"hidden": false,
"id": "autodate3332085495",
"name": "updated",
"onCreate": true,
"onUpdate": true,
"presentable": false,
"system": false,
"type": "autodate"
}
],
"id": "pbc_2452428166",
"indexes": [],
"listRule": null,
"name": "invites",
"system": false,
"type": "base",
"updateRule": null,
"viewRule": null
});
return app.save(collection);
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2452428166");
return app.delete(collection);
})

View File

@@ -0,0 +1,95 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2452428166")
// update collection data
unmarshal({
"listRule": "token = @request.query.token"
}, collection)
// remove field
collection.fields.removeById("bool2034721560")
// remove field
collection.fields.removeById("date2593941644")
// add field
collection.fields.addAt(2, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text1597481275",
"max": 0,
"min": 0,
"name": "token",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// update field
collection.fields.addAt(1, new Field({
"exceptDomains": null,
"hidden": false,
"id": "email1181691900",
"name": "email",
"onlyDomains": null,
"presentable": false,
"required": false,
"system": false,
"type": "email"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2452428166")
// update collection data
unmarshal({
"listRule": null
}, collection)
// add field
collection.fields.addAt(2, new Field({
"hidden": false,
"id": "bool2034721560",
"name": "used",
"presentable": false,
"required": false,
"system": false,
"type": "bool"
}))
// add field
collection.fields.addAt(3, new Field({
"hidden": false,
"id": "date2593941644",
"max": "",
"min": "",
"name": "expires",
"presentable": false,
"required": false,
"system": false,
"type": "date"
}))
// remove field
collection.fields.removeById("text1597481275")
// update field
collection.fields.addAt(1, new Field({
"exceptDomains": null,
"hidden": false,
"id": "email1181691900",
"name": "target",
"onlyDomains": null,
"presentable": false,
"required": false,
"system": false,
"type": "email"
}))
return app.save(collection)
})

View File

@@ -0,0 +1,29 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// add field
collection.fields.addAt(8, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text1380122564",
"max": 0,
"min": 0,
"name": "invite_token",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// remove field
collection.fields.removeById("text1380122564")
return app.save(collection)
})

View File

@@ -0,0 +1,86 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// add field
collection.fields.addAt(9, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text2335412286",
"max": 0,
"min": 0,
"name": "mcUuid",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// add field
collection.fields.addAt(10, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text74432158",
"max": 0,
"min": 0,
"name": "mcName",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// add field
collection.fields.addAt(11, new Field({
"hidden": false,
"id": "file1479911558",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "mcSkin",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// add field
collection.fields.addAt(12, new Field({
"hidden": false,
"id": "file157878879",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "mcCape",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// remove field
collection.fields.removeById("text2335412286")
// remove field
collection.fields.removeById("text74432158")
// remove field
collection.fields.removeById("file1479911558")
// remove field
collection.fields.removeById("file157878879")
return app.save(collection)
})

View File

@@ -0,0 +1,112 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = new Collection({
"createRule": null,
"deleteRule": null,
"fields": [
{
"autogeneratePattern": "[a-z0-9]{15}",
"hidden": false,
"id": "text3208210256",
"max": 15,
"min": 15,
"name": "id",
"pattern": "^[a-z0-9]+$",
"presentable": false,
"primaryKey": true,
"required": true,
"system": true,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text889886754",
"max": 0,
"min": 0,
"name": "accessToken",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text307182918",
"max": 0,
"min": 0,
"name": "clientToken",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"cascadeDelete": false,
"collectionId": "_pb_users_auth_",
"hidden": false,
"id": "relation2375276105",
"maxSelect": 1,
"minSelect": 0,
"name": "user",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text2602996892",
"max": 0,
"min": 0,
"name": "profileId",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "autodate2990389176",
"name": "created",
"onCreate": true,
"onUpdate": false,
"presentable": false,
"system": false,
"type": "autodate"
},
{
"hidden": false,
"id": "autodate3332085495",
"name": "updated",
"onCreate": true,
"onUpdate": true,
"presentable": false,
"system": false,
"type": "autodate"
}
],
"id": "pbc_3211729646",
"indexes": [],
"listRule": null,
"name": "mc_tokens",
"system": false,
"type": "base",
"updateRule": null,
"viewRule": null
});
return app.save(collection);
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_3211729646");
return app.delete(collection);
})

View File

@@ -0,0 +1,20 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_3211729646")
// update collection data
unmarshal({
"createRule": "@request.auth.id != \"\""
}, collection)
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_3211729646")
// update collection data
unmarshal({
"createRule": null
}, collection)
return app.save(collection)
})

View File

@@ -0,0 +1,38 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// update collection data
unmarshal({
"indexes": [
"CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
"CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''",
"CREATE UNIQUE INDEX `idx_gMjTpwYqPW` ON `users` (`name`)"
],
"passwordAuth": {
"identityFields": [
"email",
"name"
]
}
}, collection)
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// update collection data
unmarshal({
"indexes": [
"CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
"CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''"
],
"passwordAuth": {
"identityFields": [
"email"
]
}
}, collection)
return app.save(collection)
})

View File

@@ -0,0 +1,113 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = new Collection({
"createRule": null,
"deleteRule": null,
"fields": [
{
"autogeneratePattern": "[a-z0-9]{15}",
"hidden": false,
"id": "text3208210256",
"max": 15,
"min": 15,
"name": "id",
"pattern": "^[a-z0-9]+$",
"presentable": false,
"primaryKey": true,
"required": true,
"system": true,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text3514781862",
"max": 0,
"min": 0,
"name": "uuid",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text1579384326",
"max": 0,
"min": 0,
"name": "name",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text41510942",
"max": 0,
"min": 0,
"name": "skin",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"autogeneratePattern": "",
"hidden": false,
"id": "text1394985671",
"max": 0,
"min": 0,
"name": "cape",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
},
{
"hidden": false,
"id": "autodate2990389176",
"name": "created",
"onCreate": true,
"onUpdate": false,
"presentable": false,
"system": false,
"type": "autodate"
},
{
"hidden": false,
"id": "autodate3332085495",
"name": "updated",
"onCreate": true,
"onUpdate": true,
"presentable": false,
"system": false,
"type": "autodate"
}
],
"id": "pbc_2032083498",
"indexes": [],
"listRule": null,
"name": "mc_profiles",
"system": false,
"type": "base",
"updateRule": null,
"viewRule": null
});
return app.save(collection);
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498");
return app.delete(collection);
})

View File

@@ -0,0 +1,28 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// add field
collection.fields.addAt(5, new Field({
"cascadeDelete": false,
"collectionId": "_pb_users_auth_",
"hidden": false,
"id": "relation2375276105",
"maxSelect": 1,
"minSelect": 0,
"name": "user",
"presentable": false,
"required": false,
"system": false,
"type": "relation"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// remove field
collection.fields.removeById("relation2375276105")
return app.save(collection)
})

View File

@@ -0,0 +1,86 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// remove field
collection.fields.removeById("text41510942")
// remove field
collection.fields.removeById("text1394985671")
// add field
collection.fields.addAt(4, new Field({
"hidden": false,
"id": "file41510942",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "skin",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// add field
collection.fields.addAt(5, new Field({
"hidden": false,
"id": "file1394985671",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "cape",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// add field
collection.fields.addAt(3, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text41510942",
"max": 0,
"min": 0,
"name": "skin",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// add field
collection.fields.addAt(4, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text1394985671",
"max": 0,
"min": 0,
"name": "cape",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// remove field
collection.fields.removeById("file41510942")
// remove field
collection.fields.removeById("file1394985671")
return app.save(collection)
})

View File

@@ -0,0 +1,24 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// add field
collection.fields.addAt(6, new Field({
"hidden": false,
"id": "bool3814588639",
"name": "default",
"presentable": false,
"required": false,
"system": false,
"type": "bool"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// remove field
collection.fields.removeById("bool3814588639")
return app.save(collection)
})

View File

@@ -0,0 +1,102 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// update field
collection.fields.addAt(4, new Field({
"hidden": false,
"id": "file41510942",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [
"image/png",
"image/vnd.mozilla.apng"
],
"name": "skin",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// update field
collection.fields.addAt(5, new Field({
"hidden": false,
"id": "file1394985671",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [
"image/png",
"image/vnd.mozilla.apng"
],
"name": "cape",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// update field
collection.fields.addAt(6, new Field({
"hidden": false,
"id": "bool3814588639",
"name": "selected",
"presentable": false,
"required": false,
"system": false,
"type": "bool"
}))
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// update field
collection.fields.addAt(4, new Field({
"hidden": false,
"id": "file41510942",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "skin",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// update field
collection.fields.addAt(5, new Field({
"hidden": false,
"id": "file1394985671",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "cape",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// update field
collection.fields.addAt(6, new Field({
"hidden": false,
"id": "bool3814588639",
"name": "default",
"presentable": false,
"required": false,
"system": false,
"type": "bool"
}))
return app.save(collection)
})

View File

@@ -0,0 +1,28 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// update collection data
unmarshal({
"createRule": "@request.auth.id = user.id",
"deleteRule": "@request.auth.id = user.id",
"listRule": "@request.auth.id = user.id",
"updateRule": "@request.auth.id = user.id",
"viewRule": "@request.auth.id = user.id"
}, collection)
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("pbc_2032083498")
// update collection data
unmarshal({
"createRule": null,
"deleteRule": null,
"listRule": null,
"updateRule": null,
"viewRule": null
}, collection)
return app.save(collection)
})

View File

@@ -0,0 +1,86 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// remove field
collection.fields.removeById("text2335412286")
// remove field
collection.fields.removeById("text74432158")
// remove field
collection.fields.removeById("file1479911558")
// remove field
collection.fields.removeById("file157878879")
return app.save(collection)
}, (app) => {
const collection = app.findCollectionByNameOrId("_pb_users_auth_")
// add field
collection.fields.addAt(9, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text2335412286",
"max": 0,
"min": 0,
"name": "mcUuid",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// add field
collection.fields.addAt(10, new Field({
"autogeneratePattern": "",
"hidden": false,
"id": "text74432158",
"max": 0,
"min": 0,
"name": "mcName",
"pattern": "",
"presentable": false,
"primaryKey": false,
"required": false,
"system": false,
"type": "text"
}))
// add field
collection.fields.addAt(11, new Field({
"hidden": false,
"id": "file1479911558",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "mcSkin",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
// add field
collection.fields.addAt(12, new Field({
"hidden": false,
"id": "file157878879",
"maxSelect": 1,
"maxSize": 0,
"mimeTypes": [],
"name": "mcCape",
"presentable": false,
"protected": false,
"required": false,
"system": false,
"thumbs": [],
"type": "file"
}))
return app.save(collection)
})