Запрос на слияние 'sber_mobile' (#8) из sber_mobile в main
This commit is contained in:
222
server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt
Normal file
222
server/routers/kfu-m-24-1/sber_mobile/DB_Scheme.txt
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
-- Расширение для генерации UUID
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||||
|
|
||||||
|
-- 1. Управляющие компании
|
||||||
|
CREATE TABLE management_companies (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
logo_url TEXT,
|
||||||
|
contact_phone TEXT NOT NULL,
|
||||||
|
email TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 2. Жилые дома
|
||||||
|
CREATE TABLE buildings (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
management_company_id UUID NOT NULL REFERENCES management_companies(id),
|
||||||
|
name TEXT,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
floors INTEGER,
|
||||||
|
entrances INTEGER,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 3. Профили пользователей
|
||||||
|
CREATE TABLE user_profiles (
|
||||||
|
id UUID PRIMARY KEY REFERENCES auth.users(id),
|
||||||
|
full_name TEXT,
|
||||||
|
avatar_url TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 4. Квартиры
|
||||||
|
CREATE TABLE apartments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||||
|
number TEXT NOT NULL,
|
||||||
|
area DECIMAL(10, 2),
|
||||||
|
floor INTEGER,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 5. Связь пользователей с квартирами
|
||||||
|
CREATE TABLE apartment_residents (
|
||||||
|
apartment_id UUID NOT NULL REFERENCES apartments(id),
|
||||||
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
is_owner BOOLEAN DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (apartment_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 6. Сервисы УК
|
||||||
|
CREATE TABLE management_services (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
management_company_id UUID NOT NULL REFERENCES management_companies(id),
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
category TEXT NOT NULL,
|
||||||
|
base_price DECIMAL(10, 2),
|
||||||
|
image_url TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 7. Связь сервисов УК с домами
|
||||||
|
CREATE TABLE building_management_services (
|
||||||
|
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||||
|
service_id UUID NOT NULL REFERENCES management_services(id),
|
||||||
|
custom_price DECIMAL(10, 2),
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (building_id, service_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 8. Платежные сервисы
|
||||||
|
CREATE TABLE payment_services (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
logo_url TEXT,
|
||||||
|
provider_name TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 9. Дополнительные сервисы
|
||||||
|
CREATE TABLE additional_services (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
category TEXT NOT NULL,
|
||||||
|
price DECIMAL(10, 2),
|
||||||
|
image_url TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 10. Инициативы
|
||||||
|
CREATE TABLE initiatives (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||||
|
creator_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL CHECK (
|
||||||
|
status IN ('moderation', 'review', 'fundraising', 'approved', 'rejected')
|
||||||
|
),
|
||||||
|
target_amount DECIMAL(10, 2),
|
||||||
|
current_amount DECIMAL(10, 2) DEFAULT 0,
|
||||||
|
image_url TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 11. Голосования
|
||||||
|
CREATE TABLE votes (
|
||||||
|
initiative_id UUID NOT NULL REFERENCES initiatives(id),
|
||||||
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
vote_type TEXT NOT NULL CHECK (vote_type IN ('for', 'against')),
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
PRIMARY KEY (initiative_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 12. Чат
|
||||||
|
CREATE TABLE chats (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||||
|
name TEXT,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 13. Сообщения
|
||||||
|
CREATE TABLE messages (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
chat_id UUID NOT NULL REFERENCES chats(id),
|
||||||
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 14. Камеры
|
||||||
|
CREATE TABLE cameras (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||||
|
location TEXT NOT NULL,
|
||||||
|
stream_url TEXT NOT NULL,
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 15. Платежи ЖКХ (исправленная версия)
|
||||||
|
CREATE TABLE utility_payments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
apartment_id UUID NOT NULL REFERENCES apartments(id),
|
||||||
|
service_id UUID NOT NULL REFERENCES payment_services(id),
|
||||||
|
amount DECIMAL(10, 2) NOT NULL,
|
||||||
|
period DATE NOT NULL,
|
||||||
|
status TEXT NOT NULL CHECK (status IN ('paid', 'pending', 'overdue')),
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 16. Заявки
|
||||||
|
CREATE TABLE tickets (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL CHECK (status IN ('open', 'in_progress', 'resolved')),
|
||||||
|
category TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы
|
||||||
|
CREATE INDEX idx_buildings_management_company ON buildings(management_company_id);
|
||||||
|
CREATE INDEX idx_management_services_company ON management_services(management_company_id);
|
||||||
|
CREATE INDEX idx_building_services_building ON building_management_services(building_id);
|
||||||
|
CREATE INDEX idx_initiatives_building ON initiatives(building_id);
|
||||||
|
CREATE INDEX idx_votes_initiative ON votes(initiative_id);
|
||||||
|
CREATE INDEX idx_messages_chat ON messages(chat_id);
|
||||||
|
CREATE INDEX idx_cameras_building ON cameras(building_id);
|
||||||
|
CREATE INDEX idx_tickets_user ON tickets(user_id);
|
||||||
|
CREATE INDEX idx_apartments_building ON apartments(building_id);
|
||||||
|
CREATE INDEX idx_apartment_residents_apartment ON apartment_residents(apartment_id);
|
||||||
|
CREATE INDEX idx_apartment_residents_user ON apartment_residents(user_id);
|
||||||
|
CREATE INDEX idx_payments_apartment ON utility_payments(apartment_id);
|
||||||
|
CREATE INDEX idx_payments_service ON utility_payments(service_id);
|
||||||
|
|
||||||
|
-- Триггеры для обновления updated_at
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Применяем триггеры ко всем таблицам с updated_at
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
t record;
|
||||||
|
BEGIN
|
||||||
|
FOR t IN
|
||||||
|
SELECT table_name
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE column_name = 'updated_at'
|
||||||
|
AND table_schema = 'public'
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('CREATE TRIGGER trigger_%s_updated_at
|
||||||
|
BEFORE UPDATE ON %I
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at()',
|
||||||
|
t.table_name, t.table_name);
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
56
server/routers/kfu-m-24-1/sber_mobile/additional_services.js
Normal file
56
server/routers/kfu-m-24-1/sber_mobile/additional_services.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все дополнительные сервисы (по УК)
|
||||||
|
router.get('/additional-services', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { management_company_id } = req.query;
|
||||||
|
let query = supabase.from('additional_services').select('*');
|
||||||
|
if (management_company_id) query = query.eq('management_company_id', management_company_id);
|
||||||
|
const { data, error } = await query;
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить сервис по id
|
||||||
|
router.get('/additional-services/:id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { id } = req.params;
|
||||||
|
const { data, error } = await supabase.from('additional_services').select('*').eq('id', id).single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Создать сервис
|
||||||
|
router.post('/additional-services', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { title, description, category, price, image_url } = req.body;
|
||||||
|
const { data, error } = await supabase.from('additional_services').insert([
|
||||||
|
{ title, description, category, price, image_url }
|
||||||
|
]).select().single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обновить сервис
|
||||||
|
router.put('/additional-services/:id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { id } = req.params;
|
||||||
|
const { title, description, category, price, image_url } = req.body;
|
||||||
|
const { data, error } = await supabase.from('additional_services').update({
|
||||||
|
title, description, category, price, image_url
|
||||||
|
}).eq('id', id).select().single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Удалить сервис
|
||||||
|
router.delete('/additional-services/:id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { id } = req.params;
|
||||||
|
const { error } = await supabase.from('additional_services').delete().eq('id', id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
45
server/routers/kfu-m-24-1/sber_mobile/apartments.js
Normal file
45
server/routers/kfu-m-24-1/sber_mobile/apartments.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все квартиры по дому
|
||||||
|
router.get('/apartments', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { building_id } = req.query;
|
||||||
|
if (!building_id) return res.status(400).json({ error: 'building_id required' });
|
||||||
|
const { data, error } = await supabase.from('apartments').select('*').eq('building_id', building_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить адрес квартиры и название дома по id квартиры
|
||||||
|
router.get('/apartment-info', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { apartment_id } = req.query;
|
||||||
|
if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' });
|
||||||
|
|
||||||
|
// Получаем квартиру с building_id и номером
|
||||||
|
const { data: apartment, error: err1 } = await supabase
|
||||||
|
.from('apartments')
|
||||||
|
.select('id, number, building_id')
|
||||||
|
.eq('id', apartment_id)
|
||||||
|
.single();
|
||||||
|
if (err1) return res.status(400).json({ error: err1.message });
|
||||||
|
|
||||||
|
// Получаем дом по building_id
|
||||||
|
const { data: building, error: err2 } = await supabase
|
||||||
|
.from('buildings')
|
||||||
|
.select('id, name, address')
|
||||||
|
.eq('id', apartment.building_id)
|
||||||
|
.single();
|
||||||
|
if (err2) return res.status(400).json({ error: err2.message });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
apartment_id: apartment.id,
|
||||||
|
apartment_number: apartment.number,
|
||||||
|
building_id: building.id,
|
||||||
|
building_name: building.name,
|
||||||
|
building_address: building.address
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const getSupabaseClient = require('./supabaseClient').getSupabaseClient;
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
// POST /sign-in
|
// POST /sign-in
|
||||||
router.post('/sign-in', async (req, res) => {
|
router.post('/sign-in', async (req, res) => {
|
||||||
|
|||||||
14
server/routers/kfu-m-24-1/sber_mobile/buildings.js
Normal file
14
server/routers/kfu-m-24-1/sber_mobile/buildings.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все дома по УК
|
||||||
|
router.get('/buildings', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { management_company_id } = req.query;
|
||||||
|
if (!management_company_id) return res.status(400).json({ error: 'management_company_id required' });
|
||||||
|
const { data, error } = await supabase.from('buildings').select('*').eq('management_company_id', management_company_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
28
server/routers/kfu-m-24-1/sber_mobile/cameras.js
Normal file
28
server/routers/kfu-m-24-1/sber_mobile/cameras.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все камеры по дому
|
||||||
|
router.get('/cameras', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { building_id } = req.query;
|
||||||
|
if (!building_id) return res.status(400).json({ error: 'building_id required' });
|
||||||
|
const { data, error } = await supabase.from('cameras').select('*').eq('building_id', building_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить все камеры по квартире (через building_id)
|
||||||
|
router.get('/cameras/by-apartment', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { apartment_id } = req.query;
|
||||||
|
if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' });
|
||||||
|
// Получаем building_id квартиры и сразу камеры этого дома
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('cameras')
|
||||||
|
.select('*, apartments!inner(id, building_id)')
|
||||||
|
.eq('apartments.id', apartment_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
28
server/routers/kfu-m-24-1/sber_mobile/chats.js
Normal file
28
server/routers/kfu-m-24-1/sber_mobile/chats.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все чаты по дому
|
||||||
|
router.get('/chats', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { building_id } = req.query;
|
||||||
|
if (!building_id) return res.status(400).json({ error: 'building_id required' });
|
||||||
|
const { data, error } = await supabase.from('chats').select('*').eq('building_id', building_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить все чаты по квартире (через building_id)
|
||||||
|
router.get('/chats/by-apartment', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { apartment_id } = req.query;
|
||||||
|
if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' });
|
||||||
|
// Получаем building_id квартиры и сразу чаты этого дома
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('chats')
|
||||||
|
.select('*, apartments!inner(id, building_id)')
|
||||||
|
.eq('apartments.id', apartment_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -12,7 +12,14 @@ const getSupabaseKey = async () => {
|
|||||||
return data.features['sber_mobile'].SUPABASE_KEY.value;
|
return data.features['sber_mobile'].SUPABASE_KEY.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSupabaseServiceKey = async () => {
|
||||||
|
const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev');
|
||||||
|
const data = await response.json();
|
||||||
|
return data.features['sber_mobile'].SUPABASE_SERVICE_KEY.value;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSupabaseUrl,
|
getSupabaseUrl,
|
||||||
getSupabaseKey,
|
getSupabaseKey,
|
||||||
|
getSupabaseServiceKey
|
||||||
};
|
};
|
||||||
@@ -1,8 +1,32 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const authRouter = require('./auth');
|
const authRouter = require('./auth');
|
||||||
const supabaseRouter = require('./supabaseClient').router;
|
const { supabaseRouter } = require('./supabaseClient');
|
||||||
|
const profileRouter = require('./profile');
|
||||||
|
const initiativesRouter = require('./initiatives');
|
||||||
|
const votesRouter = require('./votes');
|
||||||
|
const additionalServicesRouter = require('./additional_services');
|
||||||
|
const chatsRouter = require('./chats');
|
||||||
|
const camerasRouter = require('./cameras');
|
||||||
|
const ticketsRouter = require('./tickets');
|
||||||
|
const messagesRouter = require('./messages');
|
||||||
|
const utilityPaymentsRouter = require('./utility_payments');
|
||||||
|
const apartmentsRouter = require('./apartments');
|
||||||
|
const buildingsRouter = require('./buildings');
|
||||||
|
const userApartmentsRouter = require('./user_apartments');
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
router.use('/auth', authRouter);
|
router.use('/auth', authRouter);
|
||||||
router.use('/supabase', supabaseRouter);
|
router.use('/supabase', supabaseRouter);
|
||||||
|
router.use('', profileRouter);
|
||||||
|
router.use('', initiativesRouter);
|
||||||
|
router.use('', votesRouter);
|
||||||
|
router.use('', additionalServicesRouter);
|
||||||
|
router.use('', chatsRouter);
|
||||||
|
router.use('', camerasRouter);
|
||||||
|
router.use('', ticketsRouter);
|
||||||
|
router.use('', messagesRouter);
|
||||||
|
router.use('', utilityPaymentsRouter);
|
||||||
|
router.use('', apartmentsRouter);
|
||||||
|
router.use('', buildingsRouter);
|
||||||
|
router.use('', userApartmentsRouter);
|
||||||
90
server/routers/kfu-m-24-1/sber_mobile/initiatives.js
Normal file
90
server/routers/kfu-m-24-1/sber_mobile/initiatives.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все инициативы (по дому)
|
||||||
|
router.get('/initiatives', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { building_id } = req.query;
|
||||||
|
let query = supabase.from('initiatives').select('*');
|
||||||
|
if (building_id) query = query.eq('building_id', building_id);
|
||||||
|
const { data, error } = await query;
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить инициативу по id (и optionally building_id)
|
||||||
|
router.get('/initiatives/:id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { id } = req.params;
|
||||||
|
const { building_id } = req.query;
|
||||||
|
let query = supabase.from('initiatives').select('*').eq('id', id);
|
||||||
|
if (building_id) query = query.eq('building_id', building_id);
|
||||||
|
const { data, error } = await query.single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Создать инициативу
|
||||||
|
router.post('/initiatives', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { building_id, creator_id, title, description, status, target_amount, image_url } = req.body;
|
||||||
|
const { data, error } = await supabase.from('initiatives').insert([
|
||||||
|
{ building_id, creator_id, title, description, status, target_amount, image_url }
|
||||||
|
]).select().single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обновить инициативу
|
||||||
|
router.put('/initiatives/:id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { id } = req.params;
|
||||||
|
const { title, description, status, target_amount, current_amount, image_url } = req.body;
|
||||||
|
const { data, error } = await supabase.from('initiatives').update({
|
||||||
|
title, description, status, target_amount, current_amount, image_url
|
||||||
|
}).eq('id', id).select().single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Удалить инициативу
|
||||||
|
router.delete('/initiatives/:id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { id } = req.params;
|
||||||
|
const { error } = await supabase.from('initiatives').delete().eq('id', id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить все инициативы по квартире с голосами пользователя
|
||||||
|
router.get('/initiatives/by-apartment', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { apartment_id, user_id } = req.query;
|
||||||
|
if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' });
|
||||||
|
// Получаем building_id квартиры
|
||||||
|
const { data: apartments, error: err1 } = await supabase
|
||||||
|
.from('apartments')
|
||||||
|
.select('building_id')
|
||||||
|
.eq('id', apartment_id)
|
||||||
|
.single();
|
||||||
|
if (err1) return res.status(400).json({ error: err1.message });
|
||||||
|
const building_id = apartments.building_id;
|
||||||
|
// Получаем инициативы этого дома с голосами пользователя (если user_id передан)
|
||||||
|
let selectStr = '*, votes:initiatives(id, votes!left(user_id, vote_type))';
|
||||||
|
if (!user_id) selectStr = '*';
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('initiatives')
|
||||||
|
.select(selectStr)
|
||||||
|
.eq('building_id', building_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
// Если user_id передан, фильтруем только голос текущего пользователя
|
||||||
|
if (user_id && data) {
|
||||||
|
data.forEach(initiative => {
|
||||||
|
initiative.user_vote = (initiative.votes || []).find(v => v.user_id === user_id) || null;
|
||||||
|
delete initiative.votes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
14
server/routers/kfu-m-24-1/sber_mobile/messages.js
Normal file
14
server/routers/kfu-m-24-1/sber_mobile/messages.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все сообщения в чате
|
||||||
|
router.get('/messages', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { chat_id } = req.query;
|
||||||
|
if (!chat_id) return res.status(400).json({ error: 'chat_id required' });
|
||||||
|
const { data, error } = await supabase.from('messages').select('*').eq('chat_id', chat_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
67
server/routers/kfu-m-24-1/sber_mobile/profile.js
Normal file
67
server/routers/kfu-m-24-1/sber_mobile/profile.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// GET /profile
|
||||||
|
router.get('/profile', async (req, res) => {
|
||||||
|
const { user_id } = req.query;
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
let { data: userData, error: userError } = await supabase.auth.admin.getUserById(user_id);
|
||||||
|
|
||||||
|
if (userError) return res.status(400).json({ error: userError.message });
|
||||||
|
|
||||||
|
let { data: profileData, error: profileError } = await supabase.from('user_profiles').select(`
|
||||||
|
id,
|
||||||
|
full_name,
|
||||||
|
avatar_url,
|
||||||
|
updated_at
|
||||||
|
`).eq('id', user_id).single();
|
||||||
|
|
||||||
|
if (profileError) return res.status(400).json({ error: profileError.message });
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
id: profileData.id,
|
||||||
|
username: profileData.full_name,
|
||||||
|
avatar_url: profileData.avatar_url,
|
||||||
|
phone: userData.user.phone,
|
||||||
|
updated_at: profileData.updated_at
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /profile
|
||||||
|
router.post('/profile', async (req, res) => {
|
||||||
|
const { user_id, data } = req.body;
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
|
||||||
|
const { data: userData, error: userError } = await supabase.auth.admin.updateUserById(
|
||||||
|
user_id,
|
||||||
|
{ phone: data.phone }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (userError) return res.status(400).json({ error: userError.message });
|
||||||
|
|
||||||
|
let { error: profileError } = await supabase.from('user_profiles').update({
|
||||||
|
full_name: data.username,
|
||||||
|
avatar_url: data.avatar_url,
|
||||||
|
// apartment: data.apartment
|
||||||
|
}).eq('id', user_id).single();
|
||||||
|
|
||||||
|
if (profileError) return res.status(400).json({ error: profileError.message });
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить управляющую компанию по квартире
|
||||||
|
router.get('/management-company', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { apartment_id } = req.query;
|
||||||
|
if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' });
|
||||||
|
const { data: apartment, error: err1 } = await supabase.from('apartments').select('building_id').eq('id', apartment_id).single();
|
||||||
|
if (err1) return res.status(400).json({ error: err1.message });
|
||||||
|
const { data: building, error: err2 } = await supabase.from('buildings').select('management_company_id').eq('id', apartment.building_id).single();
|
||||||
|
if (err2) return res.status(400).json({ error: err2.message });
|
||||||
|
const { data: company, error: err3 } = await supabase.from('management_companies').select('*').eq('id', building.management_company_id).single();
|
||||||
|
if (err3) return res.status(400).json({ error: err3.message });
|
||||||
|
res.json(company);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const { createClient } = require('@supabase/supabase-js');
|
const { createClient } = require('@supabase/supabase-js');
|
||||||
const { getSupabaseUrl, getSupabaseKey } = require('./get-constants');
|
const { getSupabaseUrl, getSupabaseKey, getSupabaseServiceKey } = require('./get-constants');
|
||||||
|
|
||||||
let supabase = null;
|
let supabase = null;
|
||||||
|
|
||||||
async function initSupabaseClient() {
|
async function initSupabaseClient() {
|
||||||
const supabaseUrl = await getSupabaseUrl();
|
const supabaseUrl = await getSupabaseUrl();
|
||||||
const supabaseAnonKey = await getSupabaseKey();
|
const supabaseAnonKey = await getSupabaseKey();
|
||||||
supabase = createClient(supabaseUrl, supabaseAnonKey);
|
const supabaseServiceRoleKey = await getSupabaseServiceKey();
|
||||||
|
supabase = createClient(supabaseUrl, supabaseServiceRoleKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSupabaseClient() {
|
function getSupabaseClient() {
|
||||||
@@ -34,5 +35,5 @@ try {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSupabaseClient,
|
getSupabaseClient,
|
||||||
router
|
supabaseRouter: router
|
||||||
};
|
};
|
||||||
14
server/routers/kfu-m-24-1/sber_mobile/tickets.js
Normal file
14
server/routers/kfu-m-24-1/sber_mobile/tickets.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все тикеты по дому
|
||||||
|
router.get('/tickets', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { building_id } = req.query;
|
||||||
|
if (!building_id) return res.status(400).json({ error: 'building_id required' });
|
||||||
|
const { data, error } = await supabase.from('tickets').select('*').eq('building_id', building_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
18
server/routers/kfu-m-24-1/sber_mobile/user_apartments.js
Normal file
18
server/routers/kfu-m-24-1/sber_mobile/user_apartments.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все квартиры пользователя
|
||||||
|
router.get('/user-apartments', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { user_id } = req.query;
|
||||||
|
if (!user_id) return res.status(400).json({ error: 'user_id required' });
|
||||||
|
const { data: links, error: err1 } = await supabase.from('apartment_residents').select('apartment_id').eq('user_id', user_id);
|
||||||
|
if (err1) return res.status(400).json({ error: err1.message });
|
||||||
|
const apartmentIds = links.map(l => l.apartment_id);
|
||||||
|
if (!apartmentIds.length) return res.json([]);
|
||||||
|
const { data, error } = await supabase.from('apartments').select('*').in('id', apartmentIds);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
17
server/routers/kfu-m-24-1/sber_mobile/utility_payments.js
Normal file
17
server/routers/kfu-m-24-1/sber_mobile/utility_payments.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все платежи по конкретной квартире с данными сервиса
|
||||||
|
router.get('/utility-payments', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { apartment_id } = req.query;
|
||||||
|
if (!apartment_id) return res.status(400).json({ error: 'apartment_id required' });
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('utility_payments')
|
||||||
|
.select('*, payment_services(*)')
|
||||||
|
.eq('apartment_id', apartment_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
44
server/routers/kfu-m-24-1/sber_mobile/votes.js
Normal file
44
server/routers/kfu-m-24-1/sber_mobile/votes.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
const router = require('express').Router();
|
||||||
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
|
||||||
|
// Получить все голоса по инициативе
|
||||||
|
router.get('/votes/:initiative_id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { initiative_id } = req.params;
|
||||||
|
const { data, error } = await supabase.from('votes').select('*').eq('initiative_id', initiative_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить голос пользователя по инициативе
|
||||||
|
router.get('/votes/:initiative_id/:user_id', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { initiative_id, user_id } = req.params;
|
||||||
|
const { data, error } = await supabase.from('votes').select('*').eq('initiative_id', initiative_id).eq('user_id', user_id).single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Получить все голоса по инициативе (через query)
|
||||||
|
router.get('/votes', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { initiative_id } = req.query;
|
||||||
|
if (!initiative_id) return res.status(400).json({ error: 'initiative_id required' });
|
||||||
|
const { data, error } = await supabase.from('votes').select('*').eq('initiative_id', initiative_id);
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Проголосовать (создать или обновить голос)
|
||||||
|
router.post('/votes', async (req, res) => {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
const { initiative_id, user_id, vote_type } = req.body;
|
||||||
|
// upsert: если голос уже есть, обновить, иначе создать
|
||||||
|
const { data, error } = await supabase.from('votes').upsert([
|
||||||
|
{ initiative_id, user_id, vote_type }
|
||||||
|
], { onConflict: ['initiative_id', 'user_id'] }).select().single();
|
||||||
|
if (error) return res.status(400).json({ error: error.message });
|
||||||
|
res.json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user