Files
Ali Reza bb59d5e9ba Initial commit: Meshkee CMS API
NestJS backend with Prisma, Docker Compose for Postgres/Redis, and deploy docs for the production VM.
2026-07-21 17:52:36 +03:30

74 lines
2.4 KiB
PL/PgSQL

-- Meshkee CMS — location reference tree (country → province → city)
CREATE TYPE city_level AS ENUM ('country', 'province', 'city');
CREATE TABLE cities (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
parent_id BIGINT,
level city_level NOT NULL,
name_fa VARCHAR(255) NOT NULL,
name_en VARCHAR(255) NOT NULL,
landline_code VARCHAR(10),
slug VARCHAR(100) NOT NULL,
sort_order INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT cities_slug_unique UNIQUE (slug),
CONSTRAINT cities_parent_id_fkey
FOREIGN KEY (parent_id) REFERENCES cities (id) ON DELETE CASCADE,
CONSTRAINT cities_slug_format CHECK (slug ~ '^[a-z0-9]+(?:-[a-z0-9]+)*$'),
CONSTRAINT cities_name_fa_nonempty CHECK (char_length(trim(name_fa)) > 0),
CONSTRAINT cities_name_en_nonempty CHECK (char_length(trim(name_en)) > 0),
CONSTRAINT cities_landline_code_nonempty_optional CHECK (
landline_code IS NULL OR char_length(trim(landline_code)) > 0
),
CONSTRAINT cities_country_root CHECK (
(level = 'country' AND parent_id IS NULL)
OR (level <> 'country' AND parent_id IS NOT NULL)
)
);
CREATE INDEX idx_cities_parent_id ON cities (parent_id);
CREATE INDEX idx_cities_level ON cities (level);
CREATE INDEX idx_cities_level_parent ON cities (level, parent_id, sort_order);
CREATE INDEX idx_cities_active ON cities (is_active) WHERE is_active = TRUE;
CREATE TRIGGER cities_set_updated_at
BEFORE UPDATE ON cities
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
CREATE OR REPLACE FUNCTION cities_validate_parent_level()
RETURNS TRIGGER AS $$
DECLARE
parent_level city_level;
BEGIN
IF NEW.level = 'country' THEN
RETURN NEW;
END IF;
SELECT level INTO parent_level FROM cities WHERE id = NEW.parent_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'parent city not found';
END IF;
IF NEW.level = 'province' AND parent_level <> 'country' THEN
RAISE EXCEPTION 'province parent must be a country';
END IF;
IF NEW.level = 'city' AND parent_level <> 'province' THEN
RAISE EXCEPTION 'city parent must be a province';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER cities_validate_parent_level
BEFORE INSERT OR UPDATE ON cities
FOR EACH ROW
EXECUTE FUNCTION cities_validate_parent_level();