Rule

Модель предназначена для хранения правил доступа, которые могут быть глобальными или специфичными для пользователей или тарифных планов.
Полиморфные ассоциации позволяют этой модели динамически применять правила к различным целевым объектам.

Node.JS (sequelize)

const { DataTypes, Sequelize } = require('sequelize');
const sequelize = new Sequelize(/* ... */);

const Rule = sequelize.define('Rule', {
  domain: {
    type: DataTypes.STRING,     // домен или ip
    allowNull: false
  },
  action: {
    type: DataTypes.BOOLEAN,    // действие (true - блокировка | false - разблокировка)
    allowNull: false
  },
  targetType: DataTypes.STRING, // тип полиморфной ассоциации (user | tariff)
  targetId: DataTypes.INTEGER,  // идентификатор полиморфной ассоциации
});

Go (GORM)

import "gorm.io/gorm"

type Rule struct {
	gorm.Model
	Domain     string  `gorm:"not null"`
	Action     bool    `gorm:"not null"`
	TargetID   uint
	TargetType string
}

func Получение списка заблокированных доменов

Node.JS

/**
 * Retrieves a list of blocked domains for a specific user.
 * @param {Object} user - The user object containing at least the user's ID.
 * @returns {Promise<Array>} - A promise that resolves with a list of blocked domains.
 */
async function getBlockedDomainsForUser(user) {
  const globalRules = await Rule.findAll({ where: { targetId: null, targetType: null } });

  const blockedDomains = new Set(globalRules.filter(rule => rule.action).map(rule => rule.domain));

  if (user.tariff && user.tariff.active) {
    const tariffRules = await Rule.findAll({ where: { targetId: user.tariffId, targetType: 'tariff' } });
    tariffRules.forEach(rule => rule.action ? blockedDomains.add(rule.domain) : blockedDomains.delete(rule.domain));
  }

  const userRules = await Rule.findAll({ where: { targetId: user.id, targetType: 'user' } });
  userRules.forEach(rule => rule.action ? blockedDomains.add(rule.domain) : blockedDomains.delete(rule.domain));

  return Array.from(blockedDomains);
}

Go

import "gorm.io/gorm"

// getBlockedDomainsForUser retrieves a list of blocked domains for a specific user.
// It accounts for global, tariff-related, and user-specific rules.
// Returns a slice of strings containing the domains and an error if any occurred.
func getBlockedDomainsForUser(db *gorm.DB, user User) ([]string, error) {
	var rules []Rule
	var blockedDomains []string

	if err := db.Where("target_id IS NULL AND target_type IS NULL").Find(&rules).Error; err != nil {
		return nil, err
	}

	for _, rule := range rules {
		if rule.Action {
			blockedDomains = append(blockedDomains, rule.Domain)
		}
	}

	if user.Tariff != nil && user.Tariff.Active {
		var tariffRules []Rule
		if err := db.Where("target_id = ? AND target_type = ?", user.TariffID, "tariff").Find(&tariffRules).Error; err != nil {
			return nil, err
		}

		for _, rule := range tariffRules {
			if rule.Action {
				blockedDomains = append(blockedDomains, rule.Domain)
			} else {
				blockedDomains = __removeDomain(blockedDomains, rule.Domain)
			}
		}
	}

	var userRules []Rule
	if err := db.Where("target_id = ? AND target_type = ?", user.ID, "user").Find(&userRules).Error; err != nil {
		return nil, err
	}

	for _, rule := range userRules {
		if rule.Action {
			blockedDomains = append(blockedDomains, rule.Domain)
		} else {
			blockedDomains = __removeDomain(blockedDomains, rule.Domain)
		}
	}

	return blockedDomains, nil
}

// __removeDomain helps remove a domain from the list.
func __removeDomain(domains []string, domain string) []string {
	var result []string
	for _, d := range domains {
		if d != domain {
			result = append(result, d)
		}
	}
	return result
}