diff --git a/backend/pb_migrations/1776343047_created_post_votes.js b/backend/pb_migrations/1776343047_created_post_votes.js
new file mode 100644
index 0000000..080f416
--- /dev/null
+++ b/backend/pb_migrations/1776343047_created_post_votes.js
@@ -0,0 +1,70 @@
+///
+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"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1267270444",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post_id",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "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_941672112",
+ "indexes": [],
+ "listRule": null,
+ "name": "post_votes",
+ "system": false,
+ "type": "base",
+ "updateRule": null,
+ "viewRule": null
+ });
+
+ return app.save(collection);
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112");
+
+ return app.delete(collection);
+})
diff --git a/backend/pb_migrations/1776343064_updated_post_votes.js b/backend/pb_migrations/1776343064_updated_post_votes.js
new file mode 100644
index 0000000..3095419
--- /dev/null
+++ b/backend/pb_migrations/1776343064_updated_post_votes.js
@@ -0,0 +1,28 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(2, new Field({
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2809058197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user_id",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // remove field
+ collection.fields.removeById("relation2809058197")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776343096_updated_post_votes.js b/backend/pb_migrations/1776343096_updated_post_votes.js
new file mode 100644
index 0000000..d63391f
--- /dev/null
+++ b/backend/pb_migrations/1776343096_updated_post_votes.js
@@ -0,0 +1,28 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(3, new Field({
+ "hidden": false,
+ "id": "select1002219032",
+ "maxSelect": 1,
+ "name": "vote_type",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "like"
+ ]
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // remove field
+ collection.fields.removeById("select1002219032")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776343107_updated_post_votes.js b/backend/pb_migrations/1776343107_updated_post_votes.js
new file mode 100644
index 0000000..b251c85
--- /dev/null
+++ b/backend/pb_migrations/1776343107_updated_post_votes.js
@@ -0,0 +1,41 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update field
+ collection.fields.addAt(3, new Field({
+ "hidden": false,
+ "id": "select1002219032",
+ "maxSelect": 1,
+ "name": "vote_type",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "like",
+ "dislike"
+ ]
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update field
+ collection.fields.addAt(3, new Field({
+ "hidden": false,
+ "id": "select1002219032",
+ "maxSelect": 1,
+ "name": "vote_type",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "like"
+ ]
+ }))
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776343360_updated_post_votes.js b/backend/pb_migrations/1776343360_updated_post_votes.js
new file mode 100644
index 0000000..784d9a7
--- /dev/null
+++ b/backend/pb_migrations/1776343360_updated_post_votes.js
@@ -0,0 +1,22 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "listRule": "",
+ "viewRule": ""
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "listRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776344533_updated_post_votes.js b/backend/pb_migrations/1776344533_updated_post_votes.js
new file mode 100644
index 0000000..00cf2c9
--- /dev/null
+++ b/backend/pb_migrations/1776344533_updated_post_votes.js
@@ -0,0 +1,24 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": "@request.auth.id != \"\"",
+ "listRule": "@request.auth.id != \"\"",
+ "viewRule": "@request.auth.id != \"\""
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": null,
+ "listRule": "",
+ "viewRule": ""
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776344561_updated_post_votes.js b/backend/pb_migrations/1776344561_updated_post_votes.js
new file mode 100644
index 0000000..0452721
--- /dev/null
+++ b/backend/pb_migrations/1776344561_updated_post_votes.js
@@ -0,0 +1,22 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "deleteRule": "@request.auth.id != \"\"",
+ "updateRule": "@request.auth.id != \"\""
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "deleteRule": null,
+ "updateRule": null
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776352196_updated_post_votes.js b/backend/pb_migrations/1776352196_updated_post_votes.js
new file mode 100644
index 0000000..fdb575e
--- /dev/null
+++ b/backend/pb_migrations/1776352196_updated_post_votes.js
@@ -0,0 +1,40 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update field
+ collection.fields.addAt(1, new Field({
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1267270444",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update field
+ collection.fields.addAt(1, new Field({
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1267270444",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post_id",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776352206_updated_post_votes.js b/backend/pb_migrations/1776352206_updated_post_votes.js
new file mode 100644
index 0000000..9f6ebce
--- /dev/null
+++ b/backend/pb_migrations/1776352206_updated_post_votes.js
@@ -0,0 +1,40 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update field
+ collection.fields.addAt(2, new Field({
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2809058197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update field
+ collection.fields.addAt(2, new Field({
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2809058197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user_id",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776353507_updated_post_votes.js b/backend/pb_migrations/1776353507_updated_post_votes.js
new file mode 100644
index 0000000..6a98e40
--- /dev/null
+++ b/backend/pb_migrations/1776353507_updated_post_votes.js
@@ -0,0 +1,28 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // remove field
+ collection.fields.removeById("relation2809058197")
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(2, new Field({
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2809058197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776353523_updated_post_votes.js b/backend/pb_migrations/1776353523_updated_post_votes.js
new file mode 100644
index 0000000..f6d88af
--- /dev/null
+++ b/backend/pb_migrations/1776353523_updated_post_votes.js
@@ -0,0 +1,28 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(3, 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_941672112")
+
+ // remove field
+ collection.fields.removeById("relation2375276105")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776353710_updated_post_votes.js b/backend/pb_migrations/1776353710_updated_post_votes.js
new file mode 100644
index 0000000..31ae603
--- /dev/null
+++ b/backend/pb_migrations/1776353710_updated_post_votes.js
@@ -0,0 +1,22 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "listRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "listRule": "@request.auth.id != \"\"",
+ "viewRule": "@request.auth.id != \"\""
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776353752_updated_post_votes.js b/backend/pb_migrations/1776353752_updated_post_votes.js
new file mode 100644
index 0000000..caeda02
--- /dev/null
+++ b/backend/pb_migrations/1776353752_updated_post_votes.js
@@ -0,0 +1,20 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": null
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": "@request.auth.id != \"\""
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776354539_updated_post_votes.js b/backend/pb_migrations/1776354539_updated_post_votes.js
new file mode 100644
index 0000000..b06cb1d
--- /dev/null
+++ b/backend/pb_migrations/1776354539_updated_post_votes.js
@@ -0,0 +1,24 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": "@request.auth.id != \"\"",
+ "listRule": "@request.auth.id != \"\"",
+ "viewRule": "@request.auth.id != \"\""
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": null,
+ "listRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776355090_deleted_post_votes.js b/backend/pb_migrations/1776355090_deleted_post_votes.js
new file mode 100644
index 0000000..5f2108e
--- /dev/null
+++ b/backend/pb_migrations/1776355090_deleted_post_votes.js
@@ -0,0 +1,97 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112");
+
+ return app.delete(collection);
+}, (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"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2375276105",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1267270444",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "hidden": false,
+ "id": "select1002219032",
+ "maxSelect": 1,
+ "name": "vote_type",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "like",
+ "dislike"
+ ]
+ },
+ {
+ "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_941672112",
+ "indexes": [],
+ "listRule": "@request.auth.id != \"\"",
+ "name": "post_votes",
+ "system": false,
+ "type": "base",
+ "updateRule": "@request.auth.id != \"\"",
+ "viewRule": "@request.auth.id != \"\""
+ });
+
+ return app.save(collection);
+})
diff --git a/backend/pb_migrations/1776355139_created_post_votes.js b/backend/pb_migrations/1776355139_created_post_votes.js
new file mode 100644
index 0000000..95925d3
--- /dev/null
+++ b/backend/pb_migrations/1776355139_created_post_votes.js
@@ -0,0 +1,57 @@
+///
+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"
+ },
+ {
+ "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_941672112",
+ "indexes": [],
+ "listRule": null,
+ "name": "post_votes",
+ "system": false,
+ "type": "base",
+ "updateRule": null,
+ "viewRule": null
+ });
+
+ return app.save(collection);
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112");
+
+ return app.delete(collection);
+})
diff --git a/backend/pb_migrations/1776355194_updated_post_votes.js b/backend/pb_migrations/1776355194_updated_post_votes.js
new file mode 100644
index 0000000..002ae8e
--- /dev/null
+++ b/backend/pb_migrations/1776355194_updated_post_votes.js
@@ -0,0 +1,28 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(1, 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_941672112")
+
+ // remove field
+ collection.fields.removeById("relation2375276105")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776355205_updated_post_votes.js b/backend/pb_migrations/1776355205_updated_post_votes.js
new file mode 100644
index 0000000..da5defe
--- /dev/null
+++ b/backend/pb_migrations/1776355205_updated_post_votes.js
@@ -0,0 +1,28 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(2, new Field({
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1519021197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // remove field
+ collection.fields.removeById("relation1519021197")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776355216_deleted_post_votes.js b/backend/pb_migrations/1776355216_deleted_post_votes.js
new file mode 100644
index 0000000..f2497be
--- /dev/null
+++ b/backend/pb_migrations/1776355216_deleted_post_votes.js
@@ -0,0 +1,83 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112");
+
+ return app.delete(collection);
+}, (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"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2375276105",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1519021197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "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_941672112",
+ "indexes": [],
+ "listRule": null,
+ "name": "post_votes",
+ "system": false,
+ "type": "base",
+ "updateRule": null,
+ "viewRule": null
+ });
+
+ return app.save(collection);
+})
diff --git a/backend/pb_migrations/1776355245_created_post_votes.js b/backend/pb_migrations/1776355245_created_post_votes.js
new file mode 100644
index 0000000..af676ff
--- /dev/null
+++ b/backend/pb_migrations/1776355245_created_post_votes.js
@@ -0,0 +1,83 @@
+///
+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"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "hidden": false,
+ "id": "relation2375276105",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "user",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "cascadeDelete": false,
+ "collectionId": "pbc_1125843985",
+ "hidden": false,
+ "id": "relation1519021197",
+ "maxSelect": 1,
+ "minSelect": 0,
+ "name": "post",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "relation"
+ },
+ {
+ "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_941672112",
+ "indexes": [],
+ "listRule": null,
+ "name": "post_votes",
+ "system": false,
+ "type": "base",
+ "updateRule": null,
+ "viewRule": null
+ });
+
+ return app.save(collection);
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112");
+
+ return app.delete(collection);
+})
diff --git a/backend/pb_migrations/1776355285_updated_post_votes.js b/backend/pb_migrations/1776355285_updated_post_votes.js
new file mode 100644
index 0000000..466885c
--- /dev/null
+++ b/backend/pb_migrations/1776355285_updated_post_votes.js
@@ -0,0 +1,29 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // add field
+ collection.fields.addAt(3, new Field({
+ "hidden": false,
+ "id": "select1002219032",
+ "maxSelect": 1,
+ "name": "vote_type",
+ "presentable": false,
+ "required": false,
+ "system": false,
+ "type": "select",
+ "values": [
+ "like",
+ "dislike"
+ ]
+ }))
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // remove field
+ collection.fields.removeById("select1002219032")
+
+ return app.save(collection)
+})
diff --git a/backend/pb_migrations/1776355306_updated_post_votes.js b/backend/pb_migrations/1776355306_updated_post_votes.js
new file mode 100644
index 0000000..5ce9cba
--- /dev/null
+++ b/backend/pb_migrations/1776355306_updated_post_votes.js
@@ -0,0 +1,24 @@
+///
+migrate((app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": "",
+ "listRule": "",
+ "viewRule": ""
+ }, collection)
+
+ return app.save(collection)
+}, (app) => {
+ const collection = app.findCollectionByNameOrId("pbc_941672112")
+
+ // update collection data
+ unmarshal({
+ "createRule": null,
+ "listRule": null,
+ "viewRule": null
+ }, collection)
+
+ return app.save(collection)
+})
diff --git a/frontend/scripts/test-votes.ts b/frontend/scripts/test-votes.ts
new file mode 100644
index 0000000..e6e2cd0
--- /dev/null
+++ b/frontend/scripts/test-votes.ts
@@ -0,0 +1,54 @@
+// Тест голосования
+import PocketBase from 'pocketbase';
+
+const PB_URL = process.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
+
+// Симулируем токен пользователя (нужно получить реальный)
+const testToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJfcGJfdXNlcnNfYXV0aF8iLCJleHAiOjE3NzY5NTI4NjUsImlkIjoidGR0Z2JuNGFrb3BsZGhvIiwicmVmcmVzaGFibGUiOnRydWUsInR5cGUiOiJhdXRoIn0.A1oacYog9de5GjZj5aNkHeRDWyjQKXvTSkEBFr4hi9Q';
+
+async function testVoting() {
+ const pb = new PocketBase(PB_URL);
+
+ // Симулируем авторизацию
+ pb.authStore.save(testToken, { id: 'tdtgbn4akopldho', email: 'redibedi2019@gmail.com' });
+
+ console.log('Auth valid:', pb.authStore.isValid);
+ console.log('User ID:', pb.authStore.model?.id);
+
+ // Тест: получить голоса поста
+ try {
+ const postId = 'test-post-id';
+ const votes = await pb.collection('post_votes').getList(1, 1000, {
+ filter: `post_id="${postId}"`,
+ });
+
+ console.log('\n--- Тест получения голосов ---');
+ console.log('Всего голосов:', votes.totalItems);
+
+ const likes = votes.items.filter(v => v.vote_type === 'like').length;
+ const dislikes = votes.items.filter(v => v.vote_type === 'dislike').length;
+ console.log('Likes:', likes);
+ console.log('Dislikes:', dislikes);
+ } catch (e) {
+ console.error('Ошибка:', e);
+ }
+
+ // Тест: создать голос
+ try {
+ console.log('\n--- Тест создания голоса ---');
+ const newVote = await pb.collection('post_votes').create({
+ post_id: 'test-post-123',
+ user_id: 'tdtgbn4akopldho',
+ vote_type: 'like',
+ });
+ console.log('Создан голос:', newVote.id);
+
+ // Удаляем тестовый голос
+ await pb.collection('post_votes').delete(newVote.id);
+ console.log('Удален тестовый голос');
+ } catch (e) {
+ console.error('Ошибка создания:', e);
+ }
+}
+
+testVoting();
\ No newline at end of file
diff --git a/frontend/src/components/base/PageHero.astro b/frontend/src/components/base/PageHero.astro
index 623bcb7..474e86a 100644
--- a/frontend/src/components/base/PageHero.astro
+++ b/frontend/src/components/base/PageHero.astro
@@ -39,7 +39,7 @@ const {
modalTarget,
bgImage = "",
minHeight = "100vh",
- headerOffset = "80px",
+ headerOffset = "10px",
layout = "default",
sideImage = "",
sideImageAlt = "",
@@ -50,7 +50,6 @@ const {
const showImage = layout === 'with-image' && sideImage;
---
-
+
+
\ No newline at end of file
diff --git a/frontend/src/components/blog/PostReactionButtons.astro b/frontend/src/components/blog/PostReactionButtons.astro
index 47d22a0..9446a03 100644
--- a/frontend/src/components/blog/PostReactionButtons.astro
+++ b/frontend/src/components/blog/PostReactionButtons.astro
@@ -9,7 +9,12 @@ const { initialLikes = 0, initialDislikes = 0, postId } = Astro.props;
---
-
@@ -97,7 +94,7 @@ const encodedUrl = encodeURIComponent(url);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
- .social-icon svg {
+ .social-icon :global(svg) {
width: 1.125rem;
height: 1.125rem;
}
diff --git a/frontend/src/components/home/Hero.astro b/frontend/src/components/home/Hero.astro
deleted file mode 100644
index bb15fe6..0000000
--- a/frontend/src/components/home/Hero.astro
+++ /dev/null
@@ -1,448 +0,0 @@
----
-import Button from '@components/base/Button.astro';
-
-const badgeText = "ЗАЩИТА ВОДИТЕЛЕЙ В СУРГУТЕ";
-const titleWhite = "Защитите свои права";
-const titleGold = "и водительское удостоверение";
-const description = "Профессиональная юридическая помощь при ДТП, спорах с ГИБДД и страховыми компаниями. Работаем на результат в судах ХМАО-Югры.";
-const btnPrimary = "Бесплатная консультация";
-const btnSecondary = "Мои услуги";
-
-const bgImageUrl = "/images/home/bg_hero.avif";
-const lawyerImageUrl = "/images/home/avtourist-surgut.avif";
----
-
-
-
-
-
-
-
-
-
- {badgeText}
-
-
-
- {titleWhite}
-
- {titleGold}
-
-
-
{description}
-
-
-
- {btnPrimary}
-
-
- {btnSecondary}
-
-
-
-
-
-
-

-
- 20+
- ЛЕТ ОПЫТА В СУДАХ
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/frontend/src/components/services/ServiceCategories.astro b/frontend/src/components/services/ServiceCategories.astro
index f596d36..d61b8ff 100644
--- a/frontend/src/components/services/ServiceCategories.astro
+++ b/frontend/src/components/services/ServiceCategories.astro
@@ -121,7 +121,7 @@ const {
description: "Полное ведение дела в суде: от подготовки искового до исполнения решения.",
price: "от 30 000 ₽",
icon: "🏛️",
- href: "/services/court-defense",
+ href: "/services/court-representation",
features: ["Подготовка иска/жалобы", "Представительство на заседаниях", "Сбор доказательств", "Исполнение решения"]
},
{
diff --git a/frontend/src/icons/ok.svg b/frontend/src/icons/ok.svg
new file mode 100644
index 0000000..2875905
--- /dev/null
+++ b/frontend/src/icons/ok.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/icons/telegram.svg b/frontend/src/icons/telegram.svg
new file mode 100644
index 0000000..f7b1bf7
--- /dev/null
+++ b/frontend/src/icons/telegram.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/icons/vk.svg b/frontend/src/icons/vk.svg
new file mode 100644
index 0000000..baee99b
--- /dev/null
+++ b/frontend/src/icons/vk.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/icons/warning.svg b/frontend/src/icons/warning.svg
new file mode 100644
index 0000000..a07ceb3
--- /dev/null
+++ b/frontend/src/icons/warning.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/icons/whatsapp.svg b/frontend/src/icons/whatsapp.svg
new file mode 100644
index 0000000..7803eaf
--- /dev/null
+++ b/frontend/src/icons/whatsapp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/layouts/ArticleLayout.astro b/frontend/src/layouts/ArticleLayout.astro
index 7f394b6..453a1c0 100644
--- a/frontend/src/layouts/ArticleLayout.astro
+++ b/frontend/src/layouts/ArticleLayout.astro
@@ -6,6 +6,7 @@ import Header from "@components/layout/header/Header.astro";
import Footer from "@components/layout/footer/Footer.astro";
import Breadcrumbs from "@components/base/Breadcrumbs.astro";
import ConsultationModal from "@components/base/ConsultationModal.astro";
+import Toast from "@components/base/Toast.astro";
import PostSocialShare from "@components/blog/PostSocialShare.astro";
import PostReactionButtons from "@components/blog/PostReactionButtons.astro";
@@ -59,6 +60,7 @@ const {
+
{breadcrumbs && breadcrumbs.length > 0 && (
diff --git a/frontend/src/layouts/Layout.astro b/frontend/src/layouts/Layout.astro
index 8a7ec81..6bc575a 100644
--- a/frontend/src/layouts/Layout.astro
+++ b/frontend/src/layouts/Layout.astro
@@ -6,6 +6,7 @@ import Header from "@components/layout/header/Header.astro";
import Footer from "@components/layout/footer/Footer.astro";
import Breadcrumbs from "@components/base/Breadcrumbs.astro";
import ConsultationModal from "@components/base/ConsultationModal.astro";
+import Toast from "@components/base/Toast.astro";
export interface Props {
title: string;
@@ -32,6 +33,7 @@ const { title, description, canonicalLink, breadcrumbs } = Astro.props;
+
{breadcrumbs && breadcrumbs.length > 0 && (
diff --git a/frontend/src/lib/pb.ts b/frontend/src/lib/pb.ts
index 210aaf2..0a181ae 100644
--- a/frontend/src/lib/pb.ts
+++ b/frontend/src/lib/pb.ts
@@ -5,7 +5,101 @@ const PB_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
export const pb = new PocketBase(PB_URL);
-pb.collection('_superusers').authRefresh().catch(() => {});
+if (typeof window !== 'undefined') {
+ const token = localStorage.getItem('auth_token');
+ const userStr = localStorage.getItem('user');
+
+ // Инициализируем куку из localStorage если её нет
+ if (token && !document.cookie.includes('pb_auth')) {
+ document.cookie = `pb_auth=${token}; path=/; max-age=${7 * 24 * 60 * 60}; SameSite=Lax`;
+ }
+
+ if (token && userStr) {
+ try {
+ const user = JSON.parse(userStr);
+ pb.authStore.save(token, user);
+ } catch (e) {
+ console.error('Failed to restore auth:', e);
+ }
+ }
+}
+
+export interface PostVotes {
+ id: string;
+ post_id: string;
+ user_id: string;
+ vote_type: 'like' | 'dislike';
+ created: string;
+ updated: string;
+}
+
+export interface VoteStats {
+ likes: number;
+ dislikes: number;
+ userVote: 'like' | 'dislike' | null;
+}
+
+export async function getPostVotes(postId: string): Promise {
+ const votes = await pb.collection('post_votes').getList(1, 1000, {
+ filter: `post_id="${postId}"`,
+ });
+
+ const likes = votes.items.filter((v) => v.vote_type === 'like').length;
+ const dislikes = votes.items.filter((v) => v.vote_type === 'dislike').length;
+
+ let userVote: 'like' | 'dislike' | null = null;
+ if (pb.authStore.isValid) {
+ const userId = pb.authStore.model?.id;
+ const userVoteRecord = votes.items.find((v) => v.user_id === userId);
+ if (userVoteRecord) {
+ userVote = userVoteRecord.vote_type as 'like' | 'dislike';
+ }
+ }
+
+ return { likes, dislikes, userVote };
+}
+
+export async function getPostVotesStats(postId: string): Promise<{ likes: number; dislikes: number }> {
+ // Получаем данные поста напрямую (likes/dislikes хранятся в самом посте)
+ const post = await pb.collection('posts').getOne(postId);
+
+ return {
+ likes: post.likes || 0,
+ dislikes: post.dislikes || 0,
+ };
+}
+
+export async function vote(postId: string, voteType: 'like' | 'dislike'): Promise {
+ const userId = pb.authStore.model?.id;
+
+ if (!userId) {
+ throw new Error('Требуется авторизация');
+ }
+
+ const existingVotes = await pb.collection('post_votes').getList(1, 1, {
+ filter: `post_id="${postId}" && user_id="${userId}"`,
+ });
+
+ if (existingVotes.items.length > 0) {
+ const existingVote = existingVotes.items[0] as unknown as PostVotes;
+
+ if (existingVote.vote_type === voteType) {
+ await pb.collection('post_votes').delete(existingVote.id);
+ } else {
+ await pb.collection('post_votes').update(existingVote.id, {
+ vote_type: voteType,
+ });
+ }
+ } else {
+ await pb.collection('post_votes').create({
+ post_id: postId,
+ user_id: userId,
+ vote_type: voteType,
+ });
+ }
+
+ return getPostVotes(postId);
+}
export async function getPosts(options?: {
page?: number;
diff --git a/frontend/src/pages/api/auth/sign-in.ts b/frontend/src/pages/api/auth/sign-in.ts
index 926a53d..7bff09d 100644
--- a/frontend/src/pages/api/auth/sign-in.ts
+++ b/frontend/src/pages/api/auth/sign-in.ts
@@ -28,10 +28,10 @@ export const POST: APIRoute = async ({ request, cookies }) => {
}), { status: 401 });
}
- cookies.set('pb_auth', JSON.stringify(pb.authStore.exportToCookie()), {
+ cookies.set('pb_auth', authData.token, {
path: '/',
- httpOnly: true,
- secure: process.env.NODE_ENV === 'production',
+ httpOnly: false,
+ secure: false,
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7,
});
diff --git a/frontend/src/pages/api/votes.ts b/frontend/src/pages/api/votes.ts
new file mode 100644
index 0000000..85c5cfe
--- /dev/null
+++ b/frontend/src/pages/api/votes.ts
@@ -0,0 +1,277 @@
+import type { APIRoute } from 'astro';
+
+const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
+
+// Валидация ID PocketBase (15 символов, буквы и цифры)
+const POCKETBASE_ID_REGEX = /^[a-z0-9]{15}$/;
+const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+export const POST: APIRoute = async ({ request, cookies }) => {
+ try {
+ // Получаем токен из куки
+ const token = cookies.get('pb_auth')?.value;
+
+ if (!token) {
+ return new Response(
+ JSON.stringify({ error: 'Требуется авторизация' }),
+ { status: 401, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ // Проверяем токен и получаем пользователя через auth-refresh
+ const authResponse = await fetch(
+ `${POCKETBASE_URL}/api/collections/users/auth-refresh`,
+ {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ }
+ );
+
+ if (!authResponse.ok) {
+ return new Response(
+ JSON.stringify({ error: 'Недействительная сессия' }),
+ { status: 401, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ const authData = await authResponse.json();
+
+ // ВАЖНО: Явно берем id, а не email
+ const userId = authData.record?.id;
+ const userEmail = authData.record?.email;
+
+ console.log('[Vote API] Auth data:', { userId, userEmail, record: authData.record });
+
+ // Защита: проверяем что это действительно ID, а не email
+ if (!userId || EMAIL_REGEX.test(userId)) {
+ console.error('[Vote API] Invalid userId (looks like email):', userId);
+ return new Response(
+ JSON.stringify({ error: 'Ошибка идентификации пользователя' }),
+ { status: 500, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ if (!POCKETBASE_ID_REGEX.test(userId)) {
+ console.error('[Vote API] Invalid userId format:', userId);
+ return new Response(
+ JSON.stringify({ error: 'Некорректный ID пользователя' }),
+ { status: 500, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ const body = await request.json();
+ const { post_id, vote_type } = body;
+
+ if (!post_id || !POCKETBASE_ID_REGEX.test(post_id)) {
+ return new Response(
+ JSON.stringify({ error: 'Некорректный post_id' }),
+ { status: 400, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ if (!vote_type || !['like', 'dislike'].includes(vote_type)) {
+ return new Response(
+ JSON.stringify({ error: 'Некорректный vote_type' }),
+ { status: 400, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ // Проверяем существующий голос
+ const existingVoteRes = await fetch(
+ `${POCKETBASE_URL}/api/collections/post_votes/records?` +
+ new URLSearchParams({
+ filter: `post="${post_id}" && user="${userId}"`,
+ }),
+ {
+ headers: { 'Authorization': `Bearer ${token}` },
+ }
+ );
+
+ let userVote: 'like' | 'dislike' | null = null;
+ let method = 'POST';
+ let url = `${POCKETBASE_URL}/api/collections/post_votes/records`;
+ let voteId = null;
+
+ if (existingVoteRes.ok) {
+ const existingData = await existingVoteRes.json();
+ if (existingData.items?.length > 0) {
+ const existing = existingData.items[0];
+ voteId = existing.id;
+
+ if (existing.vote_type === vote_type) {
+ // Удаляем голос (toggle off)
+ method = 'DELETE';
+ url = `${POCKETBASE_URL}/api/collections/post_votes/records/${voteId}`;
+ } else {
+ // Обновляем
+ method = 'PATCH';
+ url = `${POCKETBASE_URL}/api/collections/post_votes/records/${voteId}`;
+ userVote = vote_type;
+ }
+ }
+ }
+
+ // Выполняем операцию с голосом
+ const voteBody = method === 'POST' ? JSON.stringify({
+ post: post_id,
+ user: userId, // Теперь точно ID, а не email
+ vote_type,
+ }) : method === 'PATCH' ? JSON.stringify({ vote_type }) : null;
+
+ const voteRes = await fetch(url, {
+ method,
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: voteBody,
+ });
+
+ if (!voteRes.ok && method !== 'DELETE') {
+ const errorText = await voteRes.text();
+ console.error('[Vote API] Failed to save vote:', errorText);
+ throw new Error('Failed to save vote');
+ }
+
+ if (method === 'POST') userVote = vote_type;
+ if (method === 'DELETE') userVote = null;
+
+ // Получаем актуальные счетчики
+ const votesRes = await fetch(
+ `${POCKETBASE_URL}/api/collections/post_votes/records?` +
+ new URLSearchParams({
+ filter: `post="${post_id}"`,
+ fields: 'vote_type',
+ }),
+ {
+ headers: { 'Authorization': `Bearer ${token}` },
+ }
+ );
+
+ let likes = 0;
+ let dislikes = 0;
+
+ if (votesRes.ok) {
+ const votesData = await votesRes.json();
+ likes = votesData.items.filter((v: any) => v.vote_type === 'like').length;
+ dislikes = votesData.items.filter((v: any) => v.vote_type === 'dislike').length;
+ }
+
+ // Обновляем счетчики в посте
+ await fetch(
+ `${POCKETBASE_URL}/api/collections/posts/records/${post_id}`,
+ {
+ method: 'PATCH',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ likes, dislikes }),
+ }
+ );
+
+ return new Response(
+ JSON.stringify({ likes, dislikes, userVote }),
+ { status: 200, headers: { 'Content-Type': 'application/json' }}
+ );
+
+ } catch (error) {
+ console.error('[Vote API] Error:', error);
+ return new Response(
+ JSON.stringify({ error: 'Внутренняя ошибка сервера' }),
+ { status: 500, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+};
+
+export const GET: APIRoute = async ({ url, cookies }) => {
+ try {
+ const postId = url.searchParams.get('post_id');
+ const token = cookies.get('pb_auth')?.value;
+
+ if (!postId || !POCKETBASE_ID_REGEX.test(postId)) {
+ return new Response(
+ JSON.stringify({ error: 'Некорректный post_id' }),
+ { status: 400, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ // Получаем пост со счетчиками
+ const postRes = await fetch(
+ `${POCKETBASE_URL}/api/collections/posts/records/${postId}`,
+ {
+ headers: token ? { 'Authorization': `Bearer ${token}` } : {},
+ }
+ );
+
+ if (!postRes.ok) {
+ return new Response(
+ JSON.stringify({ error: 'Пост не найден' }),
+ { status: 404, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+
+ const post = await postRes.json();
+
+ // Определяем голос текущего пользователя
+ let userVote: 'like' | 'dislike' | null = null;
+
+ if (token) {
+ try {
+ // Проверяем токен
+ const authRes = await fetch(
+ `${POCKETBASE_URL}/api/collections/users/auth-refresh`,
+ {
+ method: 'POST',
+ headers: { 'Authorization': `Bearer ${token}` },
+ }
+ );
+
+ if (authRes.ok) {
+ const authData = await authRes.json();
+ const userId = authData.record?.id;
+
+ if (userId && POCKETBASE_ID_REGEX.test(userId)) {
+ const userVoteRes = await fetch(
+ `${POCKETBASE_URL}/api/collections/post_votes/records?` +
+ new URLSearchParams({
+ filter: `post="${postId}" && user="${userId}"`,
+ }),
+ {
+ headers: { 'Authorization': `Bearer ${token}` },
+ }
+ );
+
+ if (userVoteRes.ok) {
+ const userVoteData = await userVoteRes.json();
+ if (userVoteData.items?.length > 0) {
+ userVote = userVoteData.items[0].vote_type;
+ }
+ }
+ }
+ }
+ } catch (e) {
+ console.error('[Vote API] Error fetching user vote:', e);
+ }
+ }
+
+ return new Response(
+ JSON.stringify({
+ likes: post.likes || 0,
+ dislikes: post.dislikes || 0,
+ userVote,
+ }),
+ { status: 200, headers: { 'Content-Type': 'application/json' }}
+ );
+
+ } catch (error) {
+ console.error('[Vote API GET] Error:', error);
+ return new Response(
+ JSON.stringify({ error: 'Внутренняя ошибка сервера' }),
+ { status: 500, headers: { 'Content-Type': 'application/json' }}
+ );
+ }
+};
\ No newline at end of file
diff --git a/frontend/src/pages/auth/sign-in.astro b/frontend/src/pages/auth/sign-in.astro
index faeff59..f689cc5 100644
--- a/frontend/src/pages/auth/sign-in.astro
+++ b/frontend/src/pages/auth/sign-in.astro
@@ -462,10 +462,13 @@ import { SITE_URL } from '@constants';
const data = await response.json();
if (response.ok && data.token) {
- // Сохраняем токен
+ // Сохраняем токен в localStorage
localStorage.setItem('auth_token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
+ // Сохраняем токен в куку для API
+ document.cookie = `pb_auth=${data.token}; path=/; max-age=${7 * 24 * 60 * 60}; SameSite=Lax`;
+
// Перенаправляем в личный кабинет
window.location.href = '/cabinet';
} else if (data.error?.includes('подтверждён')) {
diff --git a/frontend/src/pages/blog/[slug].astro b/frontend/src/pages/blog/[slug].astro
index 3a45293..9790bbd 100644
--- a/frontend/src/pages/blog/[slug].astro
+++ b/frontend/src/pages/blog/[slug].astro
@@ -4,7 +4,7 @@ import { SITE_URL } from '@constants';
import PostCommentForm from '@components/blog/PostCommentForm.astro';
import RelatedPosts from '@components/blog/RelatedPosts.astro';
import ArticleTableOfContents from '@components/blog/ArticleTableOfContents.astro';
-import { getPostBySlug, getPosts, getPostImageUrl } from '@lib/pb';
+import { getPostBySlug, getPosts, getPostImageUrl, getPostVotesStats } from '@lib/pb';
import { marked } from 'marked';
export const prerender = false;
@@ -21,6 +21,8 @@ if (!post) {
return Astro.redirect('/blog');
}
+const { likes = 0, dislikes = 0 } = await getPostVotesStats(post.id).catch(() => ({ likes: 0, dislikes: 0 }));
+
// Конвертируем markdown в HTML
const contentHtml = marked(post.content || '');
@@ -71,8 +73,8 @@ const heroImage = getPostImageUrl(post);
readTime={post.readTime}
postId={post.id}
postUrl={currentUrl}
- initialLikes={12}
- initialDislikes={2}
+ initialLikes={likes}
+ initialDislikes={dislikes}
>
diff --git a/frontend/src/pages/index.astro b/frontend/src/pages/index.astro
index 29d71ee..1ccb240 100644
--- a/frontend/src/pages/index.astro
+++ b/frontend/src/pages/index.astro
@@ -29,6 +29,7 @@ import { SITE_URL } from '@constants';
sideImageAlt="Юрист"
bgImage="/images/home/bg_hero.avif"
minHeight="100vh"
+ headerOffset="80px"
experienceBadge={{
number: "20+",
text: "ЛЕТ ОПЫТА В СУДАХ"
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index fa687f7..1851f40 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -3,7 +3,6 @@
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"],
"compilerOptions": {
- "ignoreDeprecations": "6.0",
"baseUrl": ".",
"paths": {
"@styles/*": ["src/styles/*"],