diff --git a/backend/pb_migrations/1776199882_created_posts.js b/backend/pb_migrations/1776199882_created_posts.js
new file mode 100644
index 0000000..512ec42
--- /dev/null
+++ b/backend/pb_migrations/1776199882_created_posts.js
@@ -0,0 +1,37 @@
+///
+migrate((app) => {
+ const collection = new Collection({
+ "createRule": "@request.auth.id != \"\"",
+ "deleteRule": "@request.auth.id != \"\"",
+ "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"
+ }
+ ],
+ "id": "pbc_1125843985",
+ "indexes": [],
+ "listRule": "",
+ "name": "posts",
+ "system": false,
+ "type": "base",
+ "updateRule": "@request.auth.id != \"\"",
+ "viewRule": ""
+ });
+
+ return app.save(collection);
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985");
+
+ return app.delete(collection);
+})
diff --git a/backend/pb_migrations/1776200325_updated_posts.js b/backend/pb_migrations/1776200325_updated_posts.js
new file mode 100644
index 0000000..65e0076
--- /dev/null
+++ b/backend/pb_migrations/1776200325_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(1, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text724990059",
+ "max": 0,
+ "min": 0,
+ "name": "title",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text724990059")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200337_updated_posts.js b/backend/pb_migrations/1776200337_updated_posts.js
new file mode 100644
index 0000000..8702016
--- /dev/null
+++ b/backend/pb_migrations/1776200337_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(2, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text1843675174",
+ "max": 0,
+ "min": 0,
+ "name": "description",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text1843675174")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200351_updated_posts.js b/backend/pb_migrations/1776200351_updated_posts.js
new file mode 100644
index 0000000..942cc80
--- /dev/null
+++ b/backend/pb_migrations/1776200351_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(3, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text3182418120",
+ "max": 0,
+ "min": 0,
+ "name": "author",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text3182418120")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200365_updated_posts.js b/backend/pb_migrations/1776200365_updated_posts.js
new file mode 100644
index 0000000..53df4b0
--- /dev/null
+++ b/backend/pb_migrations/1776200365_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(4, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text105650625",
+ "max": 0,
+ "min": 0,
+ "name": "category",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text105650625")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200395_updated_posts.js b/backend/pb_migrations/1776200395_updated_posts.js
new file mode 100644
index 0000000..e265e93
--- /dev/null
+++ b/backend/pb_migrations/1776200395_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(5, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text1898540465",
+ "max": 0,
+ "min": 0,
+ "name": "categoryColor",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text1898540465")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200412_updated_posts.js b/backend/pb_migrations/1776200412_updated_posts.js
new file mode 100644
index 0000000..bc52ca3
--- /dev/null
+++ b/backend/pb_migrations/1776200412_updated_posts.js
@@ -0,0 +1,26 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(6, new Field({
+ "hidden": false,
+ "id": "date2862495610",
+ "max": "",
+ "min": "",
+ "name": "date",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "date"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("date2862495610")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200439_updated_posts.js b/backend/pb_migrations/1776200439_updated_posts.js
new file mode 100644
index 0000000..08ffdcf
--- /dev/null
+++ b/backend/pb_migrations/1776200439_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(7, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text1864383797",
+ "max": 0,
+ "min": 0,
+ "name": "readmeTime",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text1864383797")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200454_updated_posts.js b/backend/pb_migrations/1776200454_updated_posts.js
new file mode 100644
index 0000000..2171599
--- /dev/null
+++ b/backend/pb_migrations/1776200454_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(8, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text2548032275",
+ "max": 0,
+ "min": 0,
+ "name": "imageUrl",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text2548032275")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200465_updated_posts.js b/backend/pb_migrations/1776200465_updated_posts.js
new file mode 100644
index 0000000..3e6b1f0
--- /dev/null
+++ b/backend/pb_migrations/1776200465_updated_posts.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(9, new Field({
+ "autogeneratePattern": "",
+ "hidden": false,
+ "id": "text2560465762",
+ "max": 0,
+ "min": 0,
+ "name": "slug",
+ "pattern": "",
+ "presentable": false,
+ "primaryKey": false,
+ "required": false,
+ "system": false,
+ "type": "text"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("text2560465762")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200481_updated_posts.js b/backend/pb_migrations/1776200481_updated_posts.js
new file mode 100644
index 0000000..7198662
--- /dev/null
+++ b/backend/pb_migrations/1776200481_updated_posts.js
@@ -0,0 +1,24 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(10, new Field({
+ "hidden": false,
+ "id": "bool1182570132",
+ "name": "draft",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "bool"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("bool1182570132")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200499_updated_posts.js b/backend/pb_migrations/1776200499_updated_posts.js
new file mode 100644
index 0000000..d53af72
--- /dev/null
+++ b/backend/pb_migrations/1776200499_updated_posts.js
@@ -0,0 +1,26 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // add field
+ collection.fields.addAt(11, new Field({
+ "convertURLs": false,
+ "hidden": false,
+ "id": "editor4274335913",
+ "maxSize": 0,
+ "name": "content",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "editor"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // remove field
+ collection.fields.removeById("editor4274335913")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200529_updated_posts.js b/backend/pb_migrations/1776200529_updated_posts.js
new file mode 100644
index 0000000..2e4356e
--- /dev/null
+++ b/backend/pb_migrations/1776200529_updated_posts.js
@@ -0,0 +1,22 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // update collection data
+ unmarshal({
+ "listRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // update collection data
+ unmarshal({
+ "listRule": "",
+ "viewRule": ""
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776200951_updated_posts.js b/backend/pb_migrations/1776200951_updated_posts.js
new file mode 100644
index 0000000..2b8d334
--- /dev/null
+++ b/backend/pb_migrations/1776200951_updated_posts.js
@@ -0,0 +1,22 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // update collection data
+ unmarshal({
+ "listRule": " draft = false || @request.auth.id != \"\" ",
+ "viewRule": " draft = false || @request.auth.id != \"\" "
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_1125843985")
+
+ // update collection data
+ unmarshal({
+ "listRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/bun.lock b/bun.lock
index b18d6cd..8ea3467 100644
--- a/bun.lock
+++ b/bun.lock
@@ -4,6 +4,12 @@
"workspaces": {
"": {
"name": "avtourist086",
+ "dependencies": {
+ "pocketbase": "^0.26.8",
+ },
+ "devDependencies": {
+ "gray-matter": "^4.0.3",
+ },
},
"frontend": {
"name": "frontend",
@@ -15,6 +21,7 @@
"@tailwindcss/vite": "^4.2.2",
"astro": "^6.0.8",
"astro-icon": "^1.1.5",
+ "marked": "^18.0.0",
"tailwindcss": "^4.2.2",
},
"devDependencies": {
@@ -360,7 +367,7 @@
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
- "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+ "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
@@ -494,6 +501,8 @@
"escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+ "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
+
"estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="],
"estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="],
@@ -516,6 +525,8 @@
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+ "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
+
"extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
@@ -554,6 +565,8 @@
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+ "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="],
+
"h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
"hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="],
@@ -606,6 +619,8 @@
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
+ "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
+
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
@@ -618,12 +633,14 @@
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
- "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
+ "js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="],
+ "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
+
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="],
@@ -666,6 +683,8 @@
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
+ "marked": ["marked@18.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-2e7Qiv/HJSXj8rDEpgTvGKsP8yYtI9xXHKDnrftrmnrJPaFNM7VRb2YCzWaX4BP1iCJ/XPduzDJZMFoqTCcIMA=="],
+
"mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="],
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
@@ -848,6 +867,8 @@
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
+ "pocketbase": ["pocketbase@0.26.8", "", {}, "sha512-aQ/ewvS7ncvAE8wxoW10iAZu6ElgbeFpBhKPnCfvRovNzm2gW8u/sQNPGN6vNgVEagz44kK//C61oKjfa+7Low=="],
+
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
@@ -922,6 +943,8 @@
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
+ "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="],
+
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
@@ -946,6 +969,8 @@
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
+ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
+
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
"stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="],
@@ -956,6 +981,8 @@
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
+ "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="],
+
"style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="],
"style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
@@ -1108,6 +1135,8 @@
"@astrojs/language-server/@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="],
+ "@astrojs/markdown-remark/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
+
"@iconify/tools/svgo": ["svgo@3.3.3", "", { "dependencies": { "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0", "sax": "^1.5.0" }, "bin": "./bin/svgo" }, "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng=="],
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
@@ -1130,6 +1159,8 @@
"anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
+ "astro/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
+
"csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
@@ -1150,6 +1181,8 @@
"yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
+ "@astrojs/markdown-remark/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
"@iconify/tools/svgo/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
"@iconify/tools/svgo/css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
@@ -1158,6 +1191,8 @@
"@types/yauzl/@types/node/undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
+ "astro/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
diff --git a/frontend/package.json b/frontend/package.json
index fe99e65..ca99f49 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -18,6 +18,7 @@
"@tailwindcss/vite": "^4.2.2",
"astro": "^6.0.8",
"astro-icon": "^1.1.5",
+ "marked": "^18.0.0",
"tailwindcss": "^4.2.2"
},
"devDependencies": {
diff --git a/frontend/src/components/base/SearchModal.astro b/frontend/src/components/base/SearchModal.astro
index dca6f8c..8092e90 100644
--- a/frontend/src/components/base/SearchModal.astro
+++ b/frontend/src/components/base/SearchModal.astro
@@ -1,26 +1,5 @@
---
-import { getCollection } from 'astro:content';
-
-const posts = await getCollection('blog');
-
-// Сортируем и форматируем для поиска
-const searchData = posts
- .sort((a, b) => b.data.date.getTime() - a.data.date.getTime())
- .map(post => ({
- title: post.data.title,
- description: post.data.description,
- slug: post.id,
- category: post.data.category,
- categoryColor: post.data.categoryColor,
- date: post.data.date.toLocaleDateString('ru-RU', {
- day: 'numeric',
- month: 'long',
- year: 'numeric'
- })
- }));
-
const title = 'Поиск по статьям';
-const postsJson = JSON.stringify(searchData);
---
@@ -61,16 +40,33 @@ const postsJson = JSON.stringify(searchData);
-
+
\ No newline at end of file
diff --git a/frontend/src/pages/blog/page/[page].astro b/frontend/src/pages/blog/page/[page].astro
index affd526..24afb59 100644
--- a/frontend/src/pages/blog/page/[page].astro
+++ b/frontend/src/pages/blog/page/[page].astro
@@ -7,49 +7,28 @@ import BlogCard from '@components/blog/BlogCard.astro';
import Pagination from '@components/base/Pagination.astro';
import CTA from '@components/base/CTA.astro';
import SearchModal from '@components/base/SearchModal.astro';
-import { getCollection } from 'astro:content';
+import { getPosts, getAllCategories } from '@lib/pb';
-export const prerender = true;
-
-export async function getStaticPaths() {
- const posts = await getCollection('blog');
- const POSTS_PER_PAGE = 6;
- const totalPages = Math.ceil(posts.length / POSTS_PER_PAGE);
-
- return Array.from({ length: totalPages }, (_, i) => ({
- params: { page: String(i + 2) }, // Начинаем со 2-й страницы (1-я это /blog/)
- }));
-}
-
-const posts = await getCollection('blog');
-
-// Сортируем посты по дате (новые сверху)
-const sortedPosts = posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
+export const prerender = false;
const POSTS_PER_PAGE = 6;
const currentPage = Number(Astro.params.page) || 1;
-const totalPages = Math.ceil(sortedPosts.length / POSTS_PER_PAGE);
-const startIndex = (currentPage - 1) * POSTS_PER_PAGE;
-const endIndex = startIndex + POSTS_PER_PAGE;
-const paginatedPosts = sortedPosts.slice(startIndex, endIndex);
+const { posts, total, totalPages } = await getPosts({ page: currentPage, perPage: POSTS_PER_PAGE });
+const categories = await getAllCategories();
-// Форматируем дату
-const formatDate = (date: Date) => {
- return date.toLocaleDateString('ru-RU', {
+const formatDate = (date: string) => {
+ return new Date(date).toLocaleDateString('ru-RU', {
day: 'numeric',
month: 'long',
year: 'numeric'
});
};
-
-// Категории
-const categories = ['Все', ...new Set(posts.map(post => post.data.category))];
---
post.data.category))]
- {paginatedPosts.map((post: any) => (
-
+ {posts.map((post: any) => (
+
))}
@@ -179,4 +158,4 @@ const categories = ['Все', ...new Set(posts.map(post => post.data.category))]
setupAnimations();
document.addEventListener('astro:after-swap', setupAnimations);
-
+
\ No newline at end of file
diff --git a/frontend/src/pages/blog/search.astro b/frontend/src/pages/blog/search.astro
index 4a909e6..75ce409 100644
--- a/frontend/src/pages/blog/search.astro
+++ b/frontend/src/pages/blog/search.astro
@@ -3,40 +3,23 @@ import Layout from '@layouts/Layout.astro';
import { SITE_URL } from '@constants';
import BlogCard from '@components/blog/BlogCard.astro';
import SearchModal from '@components/base/SearchModal.astro';
-import { getCollection } from 'astro:content';
+import { getPosts } from '@lib/pb';
-const posts = await getCollection('blog');
-
-// Получаем параметр поиска из URL
const url = new URL(Astro.request.url);
const searchQuery = url.searchParams.get('q') || '';
+const { posts: searchResults } = searchQuery
+ ? await getPosts({ perPage: 20, search: searchQuery })
+ : { posts: [], total: 0, page: 1, totalPages: 1 };
+
const breadcrumbsItems = [
{ label: 'Главная', href: '/' },
{ label: 'Блог', href: '/blog' },
{ label: searchQuery ? `Поиск: "${searchQuery}"` : 'Поиск' }
];
-// Функция поиска по статьям
-function searchArticles(query: string, allPosts: typeof posts) {
- if (!query.trim()) return [];
-
- const lowerQuery = query.toLowerCase();
-
- return allPosts.filter(post => {
- const titleMatch = post.data.title.toLowerCase().includes(lowerQuery);
- const descriptionMatch = post.data.description.toLowerCase().includes(lowerQuery);
- const categoryMatch = post.data.category.toLowerCase().includes(lowerQuery);
-
- return titleMatch || descriptionMatch || categoryMatch;
- });
-}
-
-const searchResults = searchArticles(searchQuery, posts);
-
-// Форматируем дату
-const formatDate = (date: Date) => {
- return date.toLocaleDateString('ru-RU', {
+const formatDate = (date: string) => {
+ return new Date(date).toLocaleDateString('ru-RU', {
day: 'numeric',
month: 'long',
year: 'numeric'
@@ -89,14 +72,14 @@ const formatDate = (date: Date) => {
{searchResults.map((post: any) => (
))}
@@ -149,7 +132,6 @@ const formatDate = (date: Date) => {
}));
});
- // Автоматически открываем поиск при загрузке страницы без параметров
document.addEventListener('DOMContentLoaded', () => {
const urlParams = new URLSearchParams(window.location.search);
const query = urlParams.get('q');
@@ -338,4 +320,4 @@ const formatDate = (date: Date) => {
font-size: 1.5rem;
}
}
-
+
\ No newline at end of file
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 5a40763..1851f40 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -12,7 +12,8 @@
"@scripts/*": ["src/scripts/*"],
"@layouts/*": ["src/layouts/*"],
"@assets/*": ["src/assets/*"],
- "@pages/*": ["src/pages/*"]
+ "@pages/*": ["src/pages/*"],
+ "@lib/*": ["src/lib/*"]
}
}
}
diff --git a/package.json b/package.json
index 9195490..85b4bf7 100644
--- a/package.json
+++ b/package.json
@@ -12,5 +12,11 @@
},
"workspaces": [
"frontend"
- ]
+ ],
+ "dependencies": {
+ "pocketbase": "^0.26.8"
+ },
+ "devDependencies": {
+ "gray-matter": "^4.0.3"
+ }
}
diff --git a/scripts/create-posts-collection.ts b/scripts/create-posts-collection.ts
new file mode 100644
index 0000000..79ef382
--- /dev/null
+++ b/scripts/create-posts-collection.ts
@@ -0,0 +1,59 @@
+import PocketBase from 'pocketbase';
+
+const PB_URL = process.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
+const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL;
+const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD;
+
+async function createCollection() {
+ if (!ADMIN_EMAIL || !ADMIN_PASSWORD) {
+ console.error('❌ Укажите PB_ADMIN_EMAIL и PB_ADMIN_PASSWORD в .env');
+ process.exit(1);
+ }
+
+ const pb = new PocketBase(PB_URL);
+
+ try {
+ await pb.admins.authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);
+ console.log('✅ Подключено к PocketBase');
+ } catch (e) {
+ console.error('❌ Ошибка авторизации');
+ process.exit(1);
+ }
+
+ try {
+ const collection = await pb.collections.create({
+ name: 'posts',
+ type: 'base',
+ system: false,
+ schema: [
+ { name: 'title', type: 'text', required: true },
+ { name: 'description', type: 'text', required: true },
+ { name: 'author', type: 'text', required: false },
+ { name: 'category', type: 'text', required: false },
+ { name: 'categoryColor', type: 'text', required: false },
+ { name: 'date', type: 'date', required: true },
+ { name: 'readTime', type: 'text', required: false },
+ { name: 'imageUrl', type: 'text', required: false },
+ { name: 'slug', type: 'text', required: true },
+ { name: 'draft', type: 'bool', required: false },
+ { name: 'content', type: 'editor', required: false },
+ ],
+ listRule: '',
+ viewRule: '',
+ createRule: '@request.auth.id != ""',
+ updateRule: '@request.auth.id != ""',
+ deleteRule: '@request.auth.id != ""',
+ });
+
+ console.log('✅ Коллекция "posts" создана');
+ console.log(' ID:', collection.id);
+ } catch (e: any) {
+ if (e.data?.message?.includes('already exists')) {
+ console.log('ℹ️ Коллекция "posts" уже существует');
+ } else {
+ console.error('❌ Ошибка:', e.data || e.message);
+ }
+ }
+}
+
+createCollection();
\ No newline at end of file
diff --git a/scripts/migrate-posts.ts b/scripts/migrate-posts.ts
new file mode 100644
index 0000000..14f57af
--- /dev/null
+++ b/scripts/migrate-posts.ts
@@ -0,0 +1,82 @@
+import PocketBase from 'pocketbase';
+import fs from 'fs';
+import path from 'path';
+import matter from 'gray-matter';
+
+const PB_URL = process.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
+
+const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL;
+const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD;
+
+interface PostFrontmatter {
+ title: string;
+ description: string;
+ author: string;
+ category: string;
+ categoryColor: string;
+ date: string;
+ readTime: string;
+ imageUrl: string;
+ draft: boolean;
+}
+
+async function migrate() {
+ console.log('🔄 Миграция постов в PocketBase...\n');
+
+ if (!ADMIN_EMAIL || !ADMIN_PASSWORD) {
+ console.error('❌ Укажите PB_ADMIN_EMAIL и PB_ADMIN_PASSWORD в .env');
+ process.exit(1);
+ }
+
+ const pb = new PocketBase(PB_URL);
+
+ try {
+ await pb.admins.authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);
+ console.log('✅ Подключено к PocketBase');
+ } catch (e) {
+ console.error('❌ Ошибка авторизации в PocketBase');
+ process.exit(1);
+ }
+
+ const blogDir = path.join(process.cwd(), 'frontend/src/content/blog');
+ const files = fs.readdirSync(blogDir).filter(f => f.endsWith('.mdx'));
+
+ console.log(`📂 Найдено ${files.length} постов в ${blogDir}\n`);
+
+ let migrated = 0;
+ let skipped = 0;
+
+ for (const file of files) {
+ const filePath = path.join(blogDir, file);
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
+ const { data, content } = matter(fileContent);
+
+ const frontmatter = data as PostFrontmatter;
+ const slug = file.replace('.mdx', '');
+
+ try {
+ const record = await pb.collection('posts').create({
+ title: frontmatter.title,
+ description: frontmatter.description,
+ author: frontmatter.author,
+ category: frontmatter.category,
+ categoryColor: frontmatter.categoryColor,
+ date: frontmatter.date,
+ readTime: frontmatter.readTime,
+ imageUrl: frontmatter.imageUrl,
+ slug: slug,
+ draft: frontmatter.draft ?? false,
+ content: content,
+ });
+
+ console.log(`✅ Мигрирован: ${slug}`);
+ migrated++;
+ } catch (e: any) {
+ console.error(`❌ Ошибка миграции ${slug}:`, e.response?.data || e.message);
+ }
+ }
+
+ console.log(`\n📊 Готово! Мигрировано: ${migrated}, пропущено: ${skipped}`);
+}
+
+migrate().catch(console.error);
\ No newline at end of file