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; ---
- - + `; + + const closeBtn = toast.querySelector('.toast-close'); + closeBtn?.addEventListener('click', () => { + toast.classList.remove('show'); + toast.classList.add('hide'); + setTimeout(() => toast.remove(), 300); + }); + + container.appendChild(toast); + requestAnimationFrame(() => toast.classList.add('show')); + + if (duration > 0) { + setTimeout(() => { + toast.classList.remove('show'); + toast.classList.add('hide'); + setTimeout(() => toast.remove(), 300); + }, duration); + } + } + + async function handleVote(postId: string, voteType: 'like' | 'dislike') { + const container = document.querySelector(`[data-post-id="${postId}"]`); + if (!container) return; + + const likeBtn = container.querySelector('.like-btn') as HTMLButtonElement; + const dislikeBtn = container.querySelector('.dislike-btn') as HTMLButtonElement; const likesCount = container.querySelector('[data-count="likes"]'); const dislikesCount = container.querySelector('[data-count="dislikes"]'); - let likes = parseInt(likesCount?.textContent || '0'); - let dislikes = parseInt(dislikesCount?.textContent || '0'); - let userAction: 'like' | 'dislike' | null = null; + if (!pb.authStore.isValid) { + console.log('pb.authStore.isValid:', pb.authStore.isValid); + console.log('pb.authStore.model:', pb.authStore.model); + console.log('pb.authStore.token:', pb.authStore.token ? 'exists' : 'none'); + showToast('Войдите, чтобы голосовать', 'warning', 5000, { text: 'Войти', href: '/auth/sign-in' }); + return; + } + + try { + const formData = new FormData(); + formData.append('post_id', postId); + formData.append('vote_type', voteType); + + const response = await fetch('/api/votes', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ post_id: postId, vote_type: voteType }), + credentials: 'include', + }); + + if (!response.ok) { + const error = await response.json(); + console.error('Vote error response:', error); + throw new Error(error.message || error.details || 'Ошибка голосования'); + } + + const result = await response.json(); + + if (likesCount) likesCount.textContent = result.likes.toString(); + if (dislikesCount) dislikesCount.textContent = result.dislikes.toString(); + + if (result.userVote === 'like') { + likeBtn?.classList.add('active'); + dislikeBtn?.classList.remove('active'); + } else if (result.userVote === 'dislike') { + dislikeBtn?.classList.add('active'); + likeBtn?.classList.remove('active'); + } else { + likeBtn?.classList.remove('active'); + dislikeBtn?.classList.remove('active'); + } + + showToast('Ваш голос учтён', 'success', 2000); + } catch (error) { + console.error('Ошибка голосования:', error); + showToast('Не удалось отправить голос. Попробуйте позже.', 'error', 3000); + } + } + + document.querySelectorAll('.post-reactions').forEach((container) => { + const postId = (container as HTMLElement).dataset.postId; + const likeBtn = container.querySelector('.like-btn'); + const dislikeBtn = container.querySelector('.dislike-btn'); + + async function loadUserVote() { + if (!pb.authStore.isValid) return; + + try { + const response = await fetch(`/api/votes?post_id=${postId}`, { + credentials: 'include', + }); + if (response.ok) { + const result = await response.json(); + if (result.userVote === 'like') { + likeBtn?.classList.add('active'); + } else if (result.userVote === 'dislike') { + dislikeBtn?.classList.add('active'); + } + } + } catch (e) { + console.error('Ошибка загрузки голоса:', e); + } + } + + loadUserVote(); likeBtn?.addEventListener('click', () => { - if (userAction === 'like') { - likes--; - userAction = null; - likeBtn.classList.remove('active'); - } else { - if (userAction === 'dislike') { - dislikes--; - dislikeBtn?.classList.remove('active'); - } - likes++; - userAction = 'like'; - likeBtn.classList.add('active'); - } - if (likesCount) likesCount.textContent = likes.toString(); - if (dislikesCount) dislikesCount.textContent = dislikes.toString(); + if (postId) handleVote(postId, 'like'); }); dislikeBtn?.addEventListener('click', () => { - if (userAction === 'dislike') { - dislikes--; - userAction = null; - dislikeBtn.classList.remove('active'); - } else { - if (userAction === 'like') { - likes--; - likeBtn?.classList.remove('active'); - } - dislikes++; - userAction = 'dislike'; - dislikeBtn.classList.add('active'); - } - if (likesCount) likesCount.textContent = likes.toString(); - if (dislikesCount) dislikesCount.textContent = dislikes.toString(); + if (postId) handleVote(postId, 'dislike'); }); }); diff --git a/frontend/src/components/blog/PostSocialShare.astro b/frontend/src/components/blog/PostSocialShare.astro index a065f0d..bd95ff9 100644 --- a/frontend/src/components/blog/PostSocialShare.astro +++ b/frontend/src/components/blog/PostSocialShare.astro @@ -1,4 +1,9 @@ --- +import telegramIcon from '../../icons/telegram.svg?raw'; +import vkIcon from '../../icons/vk.svg?raw'; +import whatsappIcon from '../../icons/whatsapp.svg?raw'; +import okIcon from '../../icons/ok.svg?raw'; + interface Props { title: string; url: string; @@ -22,9 +27,7 @@ const encodedUrl = encodeURIComponent(url); class="social-icon telegram" aria-label="Поделиться в Telegram" > - - - + @@ -35,9 +38,7 @@ const encodedUrl = encodeURIComponent(url); class="social-icon vk" aria-label="Поделиться в VK" > - - - + @@ -48,9 +49,7 @@ const encodedUrl = encodeURIComponent(url); class="social-icon whatsapp" aria-label="Поделиться в WhatsApp" > - - - + @@ -61,9 +60,7 @@ const encodedUrl = encodeURIComponent(url); class="social-icon odnoklassniki" aria-label="Поделиться в Одноклассниках" > - - - +
@@ -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}

- -
- - -
-
- -
-
- Юрист -
- 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/*"],