Новые изменения в компонеты

This commit is contained in:
Web-serfer 2026-05-04 03:10:06 +05:00
parent d3d3ad92c3
commit 3411dd4931
17 changed files with 99 additions and 58 deletions

View file

@ -139,3 +139,44 @@
- URL документации PocketBase: https://pocketbase.io/docs/
- URL документации SolidJS: https://docs.solidjs.com/solid-start/getting-started
- URL документации astro-icons: https://www.astroicon.dev/getting-started/
## Переменные окружения для Production (Coolify)
### PocketBase
```
PB_POCKETBASE_URL=https://avt-back.ru
PB_ADMIN_EMAIL=redibedi2019@gmail.com
PB_ADMIN_PASSWORD=Stalin4444
```
PB_POCKETBASE_URL=https://avt-back.ru
PB_ADMIN_EMAIL=redibedi2019@gmail.com
PB_ADMIN_PASSWORD=Stalin4444
```
### SMTP (Resend)
```
SMTP_HOST=smtp.resend.com
SMTP_PORT=465
SMTP_AUTH_USER=resend
SMTP_AUTH_PASS=re_bAXEq3JU_3ezUi6XvBci7Biz48eAavW4M
FROM_EMAIL=onboarding@resend.onlinemail.me
FROM_NAME=Автоюрист Сургут
```
### Site
```
SITE_URL=https://avtourist-surgut.ru
```
### Флаг production
```
PROD=true
```
### Как добавить в Coolify
1. Откройте Coolify → ваш проект
2. Перейдите App Settings → Environment Variables
3. Добавьте каждую переменную
4. Перезапустите контейнер

View file

@ -1,7 +1,7 @@
import PocketBase from 'pocketbase';
import type { Post } from '../globalInterfaces';
const PB_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
export const pb = new PocketBase(PB_URL);

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://localhost:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://localhost:8090';
const PASSWORD_MIN_LENGTH = 8;
const PASSWORD_MAX_LENGTH = 12;
@ -62,7 +62,7 @@ export const POST: APIRoute = async ({ request }) => {
);
}
const confirmUrl = `${POCKETBASE_URL}/api/collections/users/confirm-password-reset`;
const confirmUrl = `${PB_POCKETBASE_URL}/api/collections/users/confirm-password-reset`;
const response = await fetch(confirmUrl, {
method: 'POST',

View file

@ -1,7 +1,7 @@
import type { APIRoute } from 'astro';
import { pb } from '../../../lib/pb';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
const ADMIN_EMAIL = import.meta.env.PB_ADMIN_EMAIL || 'redibedi2019@gmail.com';
const ADMIN_PASSWORD = import.meta.env.PB_ADMIN_PASSWORD || 'Stalin4444';

View file

@ -1,6 +1,6 @@
import type { APIRoute } from "astro";
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || "http://localhost:8090";
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || "http://localhost:8090";
export const GET: APIRoute = async ({ cookies, request }) => {
try {
@ -16,7 +16,7 @@ export const GET: APIRoute = async ({ cookies, request }) => {
const token = pbAuthCookie.trim();
const response = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: {

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://localhost:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://localhost:8090';
const RATE_LIMIT_MAX_REQUESTS = 3;
const RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000;
@ -77,7 +77,7 @@ export const POST: APIRoute = async ({ request }) => {
);
}
const resetUrl = `${POCKETBASE_URL}/api/collections/users/request-password-reset`;
const resetUrl = `${PB_POCKETBASE_URL}/api/collections/users/request-password-reset`;
const response = await fetch(resetUrl, {
method: 'POST',

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
export const POST: APIRoute = async ({ request }) => {
try {
@ -35,7 +35,7 @@ export const POST: APIRoute = async ({ request }) => {
}), { status: 400 });
}
const response = await fetch(`${POCKETBASE_URL}/api/collections/users/confirm-password-reset`, {
const response = await fetch(`${PB_POCKETBASE_URL}/api/collections/users/confirm-password-reset`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View file

@ -1,6 +1,6 @@
import type { APIRoute } from "astro";
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || "http://localhost:8090";
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || "http://localhost:8090";
export const PATCH: APIRoute = async ({ params, request, cookies }) => {
const pbAuthCookie = cookies.get("pb_auth")?.value;
@ -16,7 +16,7 @@ export const PATCH: APIRoute = async ({ params, request, cookies }) => {
try {
let userId: string | null = null;
const userResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: {
@ -46,7 +46,7 @@ export const PATCH: APIRoute = async ({ params, request, cookies }) => {
}
const commentResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
`${PB_POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
{
headers: { Authorization: `Bearer ${token}` },
}
@ -79,7 +79,7 @@ export const PATCH: APIRoute = async ({ params, request, cookies }) => {
}
const updateResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
`${PB_POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
{
method: "PATCH",
headers: {
@ -126,7 +126,7 @@ export const DELETE: APIRoute = async ({ params, cookies }) => {
try {
let userId: string | null = null;
const userResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: {
@ -156,7 +156,7 @@ export const DELETE: APIRoute = async ({ params, cookies }) => {
}
const commentResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
`${PB_POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
{
headers: { Authorization: `Bearer ${token}` },
}
@ -179,7 +179,7 @@ export const DELETE: APIRoute = async ({ params, cookies }) => {
}
const deleteResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
`${PB_POCKETBASE_URL}/api/collections/comments/records/${commentId}`,
{
method: "DELETE",
headers: { Authorization: `Bearer ${token}` },

View file

@ -1,6 +1,6 @@
import type { APIRoute } from "astro";
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || "http://localhost:8090";
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || "http://localhost:8090";
export const GET: APIRoute = async ({ url, cookies }) => {
const postSlug = url.searchParams.get("post_slug");
@ -35,7 +35,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
try {
const response = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records?expand=${expand}&filter=${encodeURIComponent(filter)}&sort=${sort}`,
`${PB_POCKETBASE_URL}/api/collections/comments/records?expand=${expand}&filter=${encodeURIComponent(filter)}&sort=${sort}`,
{
headers: token ? { Authorization: `Bearer ${token}` } : {},
}
@ -81,7 +81,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
let userId: string | null = null;
try {
const userResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: {
@ -137,7 +137,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
// Проверка: если это ответ на комментарий
if (parent) {
const parentCommentResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records/${parent}`,
`${PB_POCKETBASE_URL}/api/collections/comments/records/${parent}`,
{
headers: {
Authorization: `Bearer ${token}`,
@ -172,7 +172,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
// Проверяем: если родительский комментарий уже имеет ответы — запрещаем (только 1 уровень ответов)
const existingRepliesResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records?filter=parent="${parent}"&perPage=1`,
`${PB_POCKETBASE_URL}/api/collections/comments/records?filter=parent="${parent}"&perPage=1`,
{
headers: {
Authorization: `Bearer ${token}`,
@ -204,7 +204,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
}
const response = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records`,
`${PB_POCKETBASE_URL}/api/collections/comments/records`,
{
method: "POST",
headers: {

View file

@ -1,7 +1,7 @@
import type { APIRoute } from 'astro';
import nodemailer from 'nodemailer';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL; // || 'http://localhost:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL; // || 'http://localhost:8090';
const SMTP_HOST = import.meta.env.SMTP_HOST; // || 'localhost';
const SMTP_PORT = import.meta.env.SMTP_PORT; // || '1025';
const NOTIFY_EMAIL = import.meta.env.NOTIFY_EMAIL; // || 'info@avtourist.ru';
@ -88,7 +88,7 @@ export const POST: APIRoute = async ({ request }) => {
const cleanPhone = phone.replace(/\D/g, '');
const response = await fetch(
`${POCKETBASE_URL}/api/collections/consultations/records`,
`${PB_POCKETBASE_URL}/api/collections/consultations/records`,
{
method: 'POST',
headers: {

View file

@ -1,7 +1,7 @@
import type { APIRoute } from 'astro';
import crypto from 'crypto';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
const POCKETBASE_ID_REGEX = /^[a-z0-9]{15}$/;
@ -24,7 +24,7 @@ function generateVisitorHash(ip: string, userAgent: string): string {
}
async function pbRequest(method: string, path: string, body?: object) {
const url = `${POCKETBASE_URL}${path}`;
const url = `${PB_POCKETBASE_URL}${path}`;
const options: RequestInit = {
method,
headers: { 'Content-Type': 'application/json' },

View file

@ -3,7 +3,7 @@ import PocketBase from 'pocketbase';
export const GET: APIRoute = async ({ params }) => {
try {
const pb = new PocketBase(import.meta.env.POCKETBASE_URL);
const pb = new PocketBase(import.meta.env.PB_POCKETBASE_URL);
const { slug } = params;
if (!slug) {

View file

@ -1,7 +1,7 @@
import type { APIRoute } from 'astro';
import PocketBase from 'pocketbase';
const PB_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
export const GET: APIRoute = async ({ url }) => {
try {

View file

@ -1,7 +1,7 @@
import type { APIRoute } from "astro";
import { sendEmail } from "../../../lib/email";
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || "http://localhost:8090";
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || "http://localhost:8090";
const ADMIN_EMAIL = import.meta.env.PB_ADMIN_EMAIL || "redibedi2019@gmail.com";
export const POST: APIRoute = async ({ request, cookies }) => {
@ -18,7 +18,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
try {
let userId: string | null = null;
const userResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: {
@ -64,7 +64,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
}
const response = await fetch(
`${POCKETBASE_URL}/api/collections/reviews/records`,
`${PB_POCKETBASE_URL}/api/collections/reviews/records`,
{
method: "POST",
headers: {
@ -141,7 +141,7 @@ export const GET: APIRoute = async ({ url }) => {
try {
const response = await fetch(
`${POCKETBASE_URL}/api/collections/reviews/records?expand=${expand}&filter=${encodeURIComponent(filter)}&sort=${sort}`,
`${PB_POCKETBASE_URL}/api/collections/reviews/records?expand=${expand}&filter=${encodeURIComponent(filter)}&sort=${sort}`,
{}
);

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_ID_REGEX = /^[a-z0-9]{15}$/;
@ -16,7 +16,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
}
const authResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: 'POST',
headers: {
@ -61,7 +61,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
}
const existingVoteRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${review_id}")&&(user="${userId}")`,
`${PB_POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${review_id}")&&(user="${userId}")`,
{
headers: { 'Authorization': `Bearer ${token}` },
}
@ -69,7 +69,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
let userVote: 'likes' | 'dislikes' | null = null;
let method = 'POST';
let url = `${POCKETBASE_URL}/api/collections/review_votes/records`;
let url = `${PB_POCKETBASE_URL}/api/collections/review_votes/records`;
let voteId: string | null = null;
if (existingVoteRes.ok) {
@ -80,10 +80,10 @@ export const POST: APIRoute = async ({ request, cookies }) => {
if (existing.vote_type === vote_type) {
method = 'DELETE';
url = `${POCKETBASE_URL}/api/collections/review_votes/records/${voteId}`;
url = `${PB_POCKETBASE_URL}/api/collections/review_votes/records/${voteId}`;
} else {
method = 'PATCH';
url = `${POCKETBASE_URL}/api/collections/review_votes/records/${voteId}`;
url = `${PB_POCKETBASE_URL}/api/collections/review_votes/records/${voteId}`;
userVote = vote_type;
}
}
@ -114,7 +114,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
if (method === 'DELETE') userVote = null;
const votesRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${review_id}")`,
`${PB_POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${review_id}")`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
@ -132,7 +132,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
console.error('[ReviewVote API] Votes error:', errorText);
}
await fetch(`${POCKETBASE_URL}/api/collections/reviews/records/${review_id}`, {
await fetch(`${PB_POCKETBASE_URL}/api/collections/reviews/records/${review_id}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
@ -168,7 +168,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
const authHeaders = token ? { 'Authorization': `Bearer ${token}` } : {};
const votesRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${reviewId}")`,
`${PB_POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${reviewId}")`,
{ headers: authHeaders }
);
@ -188,7 +188,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
if (token) {
try {
const authRes = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
@ -201,7 +201,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
if (userId && PB_ID_REGEX.test(userId)) {
const userVoteRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${reviewId}")&&(user="${userId}")`,
`${PB_POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${reviewId}")&&(user="${userId}")`,
{
headers: { 'Authorization': `Bearer ${token}` },
}

View file

@ -1,7 +1,7 @@
import type { APIRoute } from 'astro';
import crypto from 'crypto';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
function getClientIp(request: Request): string {
const forwarded = request.headers.get('x-forwarded-for');
@ -26,7 +26,7 @@ function generateVisitorHash(ip: string, userAgent: string): string {
}
async function pbRequest(method: string, path: string, body?: object) {
const url = `${POCKETBASE_URL}${path}`;
const url = `${PB_POCKETBASE_URL}${path}`;
const options: RequestInit = {
method,
headers: {

View file

@ -1,6 +1,6 @@
import type { APIRoute } from 'astro';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://127.0.0.1:8090';
// Валидация ID PocketBase (15 символов, буквы и цифры)
const POCKETBASE_ID_REGEX = /^[a-z0-9]{15}$/;
@ -20,7 +20,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
// Проверяем токен и получаем пользователя через auth-refresh
const authResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: 'POST',
headers: {
@ -84,7 +84,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
// Проверяем существующий голос
const existingVoteRes = await fetch(
`${POCKETBASE_URL}/api/collections/post_votes/records?` +
`${PB_POCKETBASE_URL}/api/collections/post_votes/records?` +
new URLSearchParams({
filter: `post="${post_id}" && user="${userId}"`,
}),
@ -95,7 +95,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
let userVote: 'like' | 'dislike' | null = null;
let method = 'POST';
let url = `${POCKETBASE_URL}/api/collections/post_votes/records`;
let url = `${PB_POCKETBASE_URL}/api/collections/post_votes/records`;
let voteId = null;
if (existingVoteRes.ok) {
@ -107,11 +107,11 @@ export const POST: APIRoute = async ({ request, cookies }) => {
if (existing.vote_type === vote_type) {
// Удаляем голос (toggle off)
method = 'DELETE';
url = `${POCKETBASE_URL}/api/collections/post_votes/records/${voteId}`;
url = `${PB_POCKETBASE_URL}/api/collections/post_votes/records/${voteId}`;
} else {
// Обновляем
method = 'PATCH';
url = `${POCKETBASE_URL}/api/collections/post_votes/records/${voteId}`;
url = `${PB_POCKETBASE_URL}/api/collections/post_votes/records/${voteId}`;
userVote = vote_type;
}
}
@ -155,7 +155,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
// Получаем актуальные счетчики (без авторизации)
const votesRes = await fetch(
`${POCKETBASE_URL}/api/collections/post_votes/records?` +
`${PB_POCKETBASE_URL}/api/collections/post_votes/records?` +
new URLSearchParams({
filter: `post="${post_id}"`,
fields: 'vote_type',
@ -174,7 +174,7 @@ export const POST: APIRoute = async ({ request, cookies }) => {
// Обновляем счетчики в посте только если есть токен
if (token) {
await fetch(
`${POCKETBASE_URL}/api/collections/posts/records/${post_id}`,
`${PB_POCKETBASE_URL}/api/collections/posts/records/${post_id}`,
{
method: 'PATCH',
headers: {
@ -214,7 +214,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
// Получаем пост со счетчиками
const postRes = await fetch(
`${POCKETBASE_URL}/api/collections/posts/records/${postId}`,
`${PB_POCKETBASE_URL}/api/collections/posts/records/${postId}`,
{
headers: token ? { 'Authorization': `Bearer ${token}` } : {},
}
@ -231,7 +231,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
// Получаем счетчики из коллекции голосов (без авторизации)
const votesCountRes = await fetch(
`${POCKETBASE_URL}/api/collections/post_votes/records?` +
`${PB_POCKETBASE_URL}/api/collections/post_votes/records?` +
new URLSearchParams({
filter: `post="${postId}"`,
fields: 'vote_type',
@ -254,7 +254,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
try {
// Проверяем токен
const authRes = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
`${PB_POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
@ -270,7 +270,7 @@ export const GET: APIRoute = async ({ url, cookies }) => {
console.log('[Vote API] GET: Checking for existing vote with:', { post: postId, user: userId });
const userVoteRes = await fetch(
`${POCKETBASE_URL}/api/collections/post_votes/records?` +
`${PB_POCKETBASE_URL}/api/collections/post_votes/records?` +
new URLSearchParams({
filter: `post="${postId}" && user="${userId}"`,
}),