Реалізація Consent Log (журнал згод користувачів) на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Consent Log (журнал згод користувачів) на сайті
Середня
~2-3 робочих дні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація Consent Log (журналу згод користувачів) на сайті

Consent Log — незмінна база даних, яка зберігає доказ отримання згоди користувачів на обробку персональних даних. За GDPR регулятори можуть вимагати доказ того, що згода була отримана законним способом.

Вимоги GDPR до Consent Log

  • Коли була дана згода (timestamp)
  • Хто дав згоду (користувач або анонімний ідентифікатор)
  • На що саме була дана згода (конкретні категорії)
  • Версія документу, з яким користувач погодився
  • Метод отримання згоди (banner, checkbox, API)
  • IP-адреса (для прив'язки до юрисдикції)

Схема бази даних

CREATE TABLE consent_events (
    id BIGSERIAL PRIMARY KEY,
    -- Ідентифікація
    user_id BIGINT REFERENCES users(id) ON DELETE SET NULL,
    anonymous_id UUID,          -- для неавторизованих
    session_id VARCHAR(100),

    -- Дані згоди
    event_type VARCHAR(20) NOT NULL,  -- 'granted', 'denied', 'withdrawn', 'updated'
    categories JSONB NOT NULL,        -- {"analytics": true, "marketing": false, ...}
    document_version VARCHAR(20),     -- версія Privacy Policy
    method VARCHAR(30),               -- 'banner', 'settings_page', 'api', 'import'

    -- Контекст
    ip_address INET,
    user_agent TEXT,
    country_code CHAR(2),
    language_code CHAR(5),

    -- Аудит
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    -- Заборона оновлення рядків (незмінний audit log)
    updated_at TIMESTAMPTZ,
    CONSTRAINT no_updates CHECK (updated_at IS NULL)
);

-- Індекси для швидкого пошуку
CREATE INDEX idx_consent_user ON consent_events(user_id) WHERE user_id IS NOT NULL;
CREATE INDEX idx_consent_anon ON consent_events(anonymous_id) WHERE anonymous_id IS NOT NULL;
CREATE INDEX idx_consent_date ON consent_events(created_at);
CREATE INDEX idx_consent_type ON consent_events(event_type);

Запис згод

import uuid
from datetime import datetime
import hashlib

class ConsentLogger:
    def __init__(self, db, geoip):
        self.db = db
        self.geoip = geoip

    def log(self, request, categories: dict, event_type: str,
            user_id=None, document_version='v2024-03'):
        # Визначити анонімний ідентифікатор
        anonymous_id = self._get_or_create_anonymous_id(request)
        country = self.geoip.country(request.remote_addr)

        self.db.execute("""
            INSERT INTO consent_events
            (user_id, anonymous_id, session_id, event_type, categories,
             document_version, method, ip_address, user_agent, country_code, created_at)
            VALUES (%s, %s, %s, %s, %s::jsonb, %s, %s, %s, %s, %s, %s)
        """, (
            user_id,
            anonymous_id,
            request.session.get('id'),
            event_type,
            json.dumps(categories),
            document_version,
            'banner',
            request.remote_addr,
            request.user_agent.string[:500],
            country,
            datetime.utcnow()
        ))

    def _get_or_create_anonymous_id(self, request):
        cookie_id = request.cookies.get('consent_id')
        if cookie_id:
            return cookie_id
        return str(uuid.uuid4())

    def get_user_consent_history(self, user_id: int):
        return self.db.query("""
            SELECT event_type, categories, document_version, created_at, ip_address
            FROM consent_events
            WHERE user_id = %s
            ORDER BY created_at DESC
        """, (user_id,))

    def get_current_consent(self, user_id: int) -> dict:
        """Поточна згода користувача"""
        latest = self.db.query_one("""
            SELECT categories FROM consent_events
            WHERE user_id = %s AND event_type IN ('granted', 'updated')
            ORDER BY created_at DESC
            LIMIT 1
        """, (user_id,))
        return latest['categories'] if latest else {}

API для користувача: перегляд та управління

@app.route('/api/my/consent', methods=['GET'])
@login_required
def get_my_consent():
    """Поточна згода користувача"""
    current = consent_logger.get_current_consent(current_user.id)
    history = consent_logger.get_user_consent_history(current_user.id)

    return jsonify({
        'current': current,
        'history': [{
            'event': r['event_type'],
            'categories': r['categories'],
            'version': r['document_version'],
            'date': r['created_at'].isoformat(),
        } for r in history[:10]]
    })

@app.route('/api/my/consent', methods=['DELETE'])
@login_required
def withdraw_consent():
    """Відозвати згоду на маркетингову обробку"""
    consent_logger.log(
        request,
        categories={'analytics': False, 'marketing': False, 'preferences': False},
        event_type='withdrawn',
        user_id=current_user.id
    )
    # Видалити з маркетингових систем
    revoke_from_mailchimp(current_user.email)
    revoke_from_facebook_custom_audience(current_user.email)

    return jsonify({'status': 'withdrawn'})