mirror of
https://git.meshkee.com/Meshkee/backend.git
synced 2026-08-11 22:30:59 +04:30
Initial commit: Meshkee CMS API
NestJS backend with Prisma, Docker Compose for Postgres/Redis, and deploy docs for the production VM.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
-- 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();
|
||||
Reference in New Issue
Block a user