This commit is contained in:
@@ -2,7 +2,7 @@ CREATE TABLE IF NOT EXISTS entities (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT,
|
||||
slug TEXT UNIQUE,
|
||||
description TEXT,
|
||||
status SMALLINT,
|
||||
time_start INT,
|
||||
@@ -12,6 +12,9 @@ CREATE TABLE IF NOT EXISTS entities (
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_entities_slug_not_deleted
|
||||
ON entities(slug)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE INDEX idx_entities_name_search
|
||||
ON entities USING GIN (name gin_trgm_ops);
|
||||
|
||||
@@ -4,12 +4,16 @@ CREATE TABLE IF NOT EXISTS wikis (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
title TEXT,
|
||||
slug TEXT,
|
||||
content TEXT,
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_wikis_slug_not_deleted
|
||||
ON wikis(slug)
|
||||
WHERE is_deleted = false;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS entity_wikis (
|
||||
entity_id UUID REFERENCES entities(id) ON DELETE CASCADE,
|
||||
|
||||
@@ -60,3 +60,8 @@ WHERE project_id = $1 AND is_deleted = false;
|
||||
UPDATE entities
|
||||
SET is_deleted = true
|
||||
WHERE id = ANY($1::uuid[]);
|
||||
|
||||
-- name: GetEntityBySlug :one
|
||||
SELECT *
|
||||
FROM entities
|
||||
WHERE slug = $1 AND is_deleted = false;
|
||||
@@ -1,8 +1,8 @@
|
||||
-- name: CreateWiki :one
|
||||
INSERT INTO wikis (
|
||||
id, title, content, project_id
|
||||
id, title, slug, content, project_id
|
||||
) VALUES (
|
||||
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3
|
||||
COALESCE(sqlc.narg('id')::uuid, uuidv7()), $1, $2, $3, $4
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
@@ -15,6 +15,7 @@ WHERE id = $1 AND is_deleted = false;
|
||||
UPDATE wikis
|
||||
SET
|
||||
title = COALESCE(sqlc.narg('title'), title),
|
||||
slug = COALESCE(sqlc.narg('slug'), slug),
|
||||
content = COALESCE(sqlc.narg('content'), content),
|
||||
project_id = COALESCE(sqlc.narg('project_id'), project_id)
|
||||
WHERE id = sqlc.arg('id') AND is_deleted = false
|
||||
@@ -84,3 +85,8 @@ WHERE wiki_id = $1;
|
||||
-- name: DeleteEntityWiki :exec
|
||||
DELETE FROM entity_wikis
|
||||
WHERE entity_id = $1 AND wiki_id = $2;
|
||||
|
||||
-- name: GetWikiBySlug :one
|
||||
SELECT *
|
||||
FROM wikis
|
||||
WHERE slug = $1 AND is_deleted = false;
|
||||
|
||||
@@ -87,7 +87,7 @@ CREATE TABLE IF NOT EXISTS entities (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT,
|
||||
slug TEXT UNIQUE,
|
||||
description TEXT,
|
||||
status SMALLINT,
|
||||
time_start INT,
|
||||
@@ -101,6 +101,7 @@ CREATE TABLE IF NOT EXISTS wikis (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
title TEXT,
|
||||
slug TEXT UNIQUE,
|
||||
content TEXT,
|
||||
is_deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
@@ -114,8 +115,6 @@ CREATE TABLE IF NOT EXISTS entity_wikis (
|
||||
PRIMARY KEY (entity_id, wiki_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_entity_wikis_project_id ON entity_wikis(project_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS geometries (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
geo_type SMALLINT NOT NULL DEFAULT 1,
|
||||
@@ -137,8 +136,6 @@ CREATE TABLE IF NOT EXISTS entity_geometries (
|
||||
PRIMARY KEY (entity_id, geometry_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_entity_geometries_project_id ON entity_geometries(project_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS commits (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
|
||||
160
docs/docs.go
160
docs/docs.go
@@ -403,7 +403,7 @@ const docTemplate = `{
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Ask a history question based on project context or global knowledge using RAG",
|
||||
@@ -516,6 +516,82 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/entities/slug/exists": {
|
||||
"get": {
|
||||
"description": "Check if a given slug already exists for entities",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Entities"
|
||||
],
|
||||
"summary": "Check entity slug existence",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Slug to check",
|
||||
"name": "slug",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/entities/slug/{slug}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific entity by its unique slug",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Entities"
|
||||
],
|
||||
"summary": "Get entity by slug",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Entity Slug",
|
||||
"name": "slug",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/entities/{id}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific entity",
|
||||
@@ -3519,6 +3595,82 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wikis/slug/exists": {
|
||||
"get": {
|
||||
"description": "Check if a given slug already exists for wikis",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Wikis"
|
||||
],
|
||||
"summary": "Check wiki slug existence",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Slug to check",
|
||||
"name": "slug",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wikis/slug/{slug}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific wiki by its unique slug",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Wikis"
|
||||
],
|
||||
"summary": "Get wiki by slug",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Wiki Slug",
|
||||
"name": "slug",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wikis/{id}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific wiki",
|
||||
@@ -3847,7 +3999,8 @@ const docTemplate = `{
|
||||
"history-api_internal_dtos_request.EntitySnapshot": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"base_hash": {
|
||||
@@ -4360,6 +4513,9 @@ const docTemplate = `{
|
||||
"reference"
|
||||
]
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
@@ -396,7 +396,7 @@
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Ask a history question based on project context or global knowledge using RAG",
|
||||
@@ -509,6 +509,82 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/entities/slug/exists": {
|
||||
"get": {
|
||||
"description": "Check if a given slug already exists for entities",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Entities"
|
||||
],
|
||||
"summary": "Check entity slug existence",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Slug to check",
|
||||
"name": "slug",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/entities/slug/{slug}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific entity by its unique slug",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Entities"
|
||||
],
|
||||
"summary": "Get entity by slug",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Entity Slug",
|
||||
"name": "slug",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/entities/{id}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific entity",
|
||||
@@ -3512,6 +3588,82 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wikis/slug/exists": {
|
||||
"get": {
|
||||
"description": "Check if a given slug already exists for wikis",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Wikis"
|
||||
],
|
||||
"summary": "Check wiki slug existence",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Slug to check",
|
||||
"name": "slug",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wikis/slug/{slug}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific wiki by its unique slug",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Wikis"
|
||||
],
|
||||
"summary": "Get wiki by slug",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Wiki Slug",
|
||||
"name": "slug",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/history-api_internal_dtos_response.CommonResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/wikis/{id}": {
|
||||
"get": {
|
||||
"description": "Get detailed information about a specific wiki",
|
||||
@@ -3840,7 +3992,8 @@
|
||||
"history-api_internal_dtos_request.EntitySnapshot": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id"
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"base_hash": {
|
||||
@@ -4353,6 +4506,9 @@
|
||||
"reference"
|
||||
]
|
||||
},
|
||||
"slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
@@ -232,6 +232,7 @@ definitions:
|
||||
type: number
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
type: object
|
||||
history-api_internal_dtos_request.EntityWikiLinkSnapshot:
|
||||
properties:
|
||||
@@ -550,6 +551,8 @@ definitions:
|
||||
- delete
|
||||
- reference
|
||||
type: string
|
||||
slug:
|
||||
type: string
|
||||
source:
|
||||
enum:
|
||||
- inline
|
||||
@@ -899,7 +902,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
- BearerAuth: []
|
||||
summary: Ask the AI chatbot
|
||||
tags:
|
||||
- Chatbot
|
||||
@@ -963,6 +966,57 @@ paths:
|
||||
summary: Get entity by ID
|
||||
tags:
|
||||
- Entities
|
||||
/entities/slug/{slug}:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get detailed information about a specific entity by its unique
|
||||
slug
|
||||
parameters:
|
||||
- description: Entity Slug
|
||||
in: path
|
||||
name: slug
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
summary: Get entity by slug
|
||||
tags:
|
||||
- Entities
|
||||
/entities/slug/exists:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Check if a given slug already exists for entities
|
||||
parameters:
|
||||
- description: Slug to check
|
||||
in: query
|
||||
name: slug
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
summary: Check entity slug existence
|
||||
tags:
|
||||
- Entities
|
||||
/geometries:
|
||||
get:
|
||||
consumes:
|
||||
@@ -2902,6 +2956,56 @@ paths:
|
||||
summary: Get wiki by ID
|
||||
tags:
|
||||
- Wikis
|
||||
/wikis/slug/{slug}:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get detailed information about a specific wiki by its unique slug
|
||||
parameters:
|
||||
- description: Wiki Slug
|
||||
in: path
|
||||
name: slug
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
summary: Get wiki by slug
|
||||
tags:
|
||||
- Wikis
|
||||
/wikis/slug/exists:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Check if a given slug already exists for wikis
|
||||
parameters:
|
||||
- description: Slug to check
|
||||
in: query
|
||||
name: slug
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/history-api_internal_dtos_response.CommonResponse'
|
||||
summary: Check wiki slug existence
|
||||
tags:
|
||||
- Wikis
|
||||
securityDefinitions:
|
||||
BearerAuth:
|
||||
description: Type "Bearer " followed by a space and JWT token.
|
||||
|
||||
@@ -46,6 +46,60 @@ func (h *EntityController) GetEntityById(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// GetEntityBySlug handles fetching a single entity by slug.
|
||||
// @Summary Get entity by slug
|
||||
// @Description Get detailed information about a specific entity by its unique slug
|
||||
// @Tags Entities
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param slug path string true "Entity Slug"
|
||||
// @Success 200 {object} response.CommonResponse
|
||||
// @Failure 404 {object} response.CommonResponse
|
||||
// @Router /entities/slug/{slug} [get]
|
||||
func (h *EntityController) GetEntityBySlug(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
slug := c.Params("slug")
|
||||
res, err := h.service.GetEntityBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
Message: err.Message,
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(response.CommonResponse{
|
||||
Status: true,
|
||||
Data: res,
|
||||
})
|
||||
}
|
||||
|
||||
// IsExistEntitySlug checks if an entity slug already exists.
|
||||
// @Summary Check entity slug existence
|
||||
// @Description Check if a given slug already exists for entities
|
||||
// @Tags Entities
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param slug query string true "Slug to check"
|
||||
// @Success 200 {object} response.CommonResponse
|
||||
// @Failure 400 {object} response.CommonResponse
|
||||
// @Router /entities/slug/exists [get]
|
||||
func (h *EntityController) IsExistEntitySlug(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
slug := c.Query("slug")
|
||||
exists, err := h.service.IsExistEntitySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
Message: err.Message,
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(response.CommonResponse{
|
||||
Status: true,
|
||||
Data: map[string]bool{"exists": exists},
|
||||
})
|
||||
}
|
||||
|
||||
// SearchEntities handles searching for entities.
|
||||
// @Summary Search entities
|
||||
// @Description Search entities with cursor pagination
|
||||
@@ -81,3 +135,4 @@ func (h *EntityController) SearchEntities(c fiber.Ctx) error {
|
||||
Data: res,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,60 @@ func (h *WikiController) GetWikiById(c fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// GetWikiBySlug handles fetching a single wiki by slug.
|
||||
// @Summary Get wiki by slug
|
||||
// @Description Get detailed information about a specific wiki by its unique slug
|
||||
// @Tags Wikis
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param slug path string true "Wiki Slug"
|
||||
// @Success 200 {object} response.CommonResponse
|
||||
// @Failure 404 {object} response.CommonResponse
|
||||
// @Router /wikis/slug/{slug} [get]
|
||||
func (h *WikiController) GetWikiBySlug(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
slug := c.Params("slug")
|
||||
res, err := h.service.GetWikiBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
Message: err.Message,
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(response.CommonResponse{
|
||||
Status: true,
|
||||
Data: res,
|
||||
})
|
||||
}
|
||||
|
||||
// IsExistWikiSlug checks if a wiki slug already exists.
|
||||
// @Summary Check wiki slug existence
|
||||
// @Description Check if a given slug already exists for wikis
|
||||
// @Tags Wikis
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param slug query string true "Slug to check"
|
||||
// @Success 200 {object} response.CommonResponse
|
||||
// @Failure 400 {object} response.CommonResponse
|
||||
// @Router /wikis/slug/exists [get]
|
||||
func (h *WikiController) IsExistWikiSlug(c fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
slug := c.Query("slug")
|
||||
exists, err := h.service.IsExistWikiSlug(ctx, slug)
|
||||
if err != nil {
|
||||
return c.Status(err.Code).JSON(response.CommonResponse{
|
||||
Status: false,
|
||||
Message: err.Message,
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(response.CommonResponse{
|
||||
Status: true,
|
||||
Data: map[string]bool{"exists": exists},
|
||||
})
|
||||
}
|
||||
|
||||
// SearchWikis handles searching for wikis.
|
||||
// @Summary Search wikis
|
||||
// @Description Search wikis with cursor pagination
|
||||
@@ -81,3 +135,4 @@ func (h *WikiController) SearchWikis(c fiber.Ctx) error {
|
||||
Data: res,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ type EntitySnapshot struct {
|
||||
ID string `json:"id" validate:"required,uuidv7"`
|
||||
Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"`
|
||||
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Slug *string `json:"slug,omitempty"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Slug *string `json:"slug" validate:"omitempty,slug"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Status *int `json:"status,omitempty" validate:"omitempty,oneof=0 1"`
|
||||
TimeStart *float64 `json:"time_start,omitempty"`
|
||||
@@ -79,12 +79,13 @@ type GeometryEntitySnapshot struct {
|
||||
}
|
||||
|
||||
type WikiSnapshot struct {
|
||||
ID string `json:"id" validate:"required,uuidv7"`
|
||||
Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"`
|
||||
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"`
|
||||
Title string `json:"title" validate:"required"`
|
||||
Doc string `json:"doc,omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
ID string `json:"id" validate:"required,uuidv7"`
|
||||
Source string `json:"source,omitempty" validate:"omitempty,oneof=inline ref"`
|
||||
Operation string `json:"operation,omitempty" validate:"omitempty,oneof=create update delete reference"`
|
||||
Title string `json:"title" validate:"required"`
|
||||
Slug *string `json:"slug" validate:"omitempty,slug"`
|
||||
Doc string `json:"doc,omitempty" validate:"omitempty"`
|
||||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
type EntityWikiLinkSnapshot struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
type WikiResponse struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
ProjectID string `json:"project_id"`
|
||||
IsDeleted bool `json:"is_deleted,omitempty"`
|
||||
|
||||
@@ -181,6 +181,31 @@ func (q *Queries) GetEntityById(ctx context.Context, id pgtype.UUID) (Entity, er
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getEntityBySlug = `-- name: GetEntityBySlug :one
|
||||
SELECT id, project_id, name, slug, description, status, time_start, time_end, is_deleted, created_at, updated_at
|
||||
FROM entities
|
||||
WHERE slug = $1 AND is_deleted = false
|
||||
`
|
||||
|
||||
func (q *Queries) GetEntityBySlug(ctx context.Context, slug pgtype.Text) (Entity, error) {
|
||||
row := q.db.QueryRow(ctx, getEntityBySlug, slug)
|
||||
var i Entity
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Name,
|
||||
&i.Slug,
|
||||
&i.Description,
|
||||
&i.Status,
|
||||
&i.TimeStart,
|
||||
&i.TimeEnd,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const searchEntities = `-- name: SearchEntities :many
|
||||
SELECT id, project_id, name, slug, description, status, time_start, time_end, is_deleted, created_at, updated_at
|
||||
FROM entities
|
||||
|
||||
@@ -183,6 +183,7 @@ type Wiki struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
ProjectID pgtype.UUID `json:"project_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Slug pgtype.Text `json:"slug"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
|
||||
@@ -68,15 +68,16 @@ func (q *Queries) CreateEntityWikis(ctx context.Context, arg CreateEntityWikisPa
|
||||
|
||||
const createWiki = `-- name: CreateWiki :one
|
||||
INSERT INTO wikis (
|
||||
id, title, content, project_id
|
||||
id, title, slug, content, project_id
|
||||
) VALUES (
|
||||
COALESCE($4::uuid, uuidv7()), $1, $2, $3
|
||||
COALESCE($5::uuid, uuidv7()), $1, $2, $3, $4
|
||||
)
|
||||
RETURNING id, project_id, title, content, is_deleted, created_at, updated_at
|
||||
RETURNING id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateWikiParams struct {
|
||||
Title pgtype.Text `json:"title"`
|
||||
Slug pgtype.Text `json:"slug"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
ProjectID pgtype.UUID `json:"project_id"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
@@ -85,6 +86,7 @@ type CreateWikiParams struct {
|
||||
func (q *Queries) CreateWiki(ctx context.Context, arg CreateWikiParams) (Wiki, error) {
|
||||
row := q.db.QueryRow(ctx, createWiki,
|
||||
arg.Title,
|
||||
arg.Slug,
|
||||
arg.Content,
|
||||
arg.ProjectID,
|
||||
arg.ID,
|
||||
@@ -94,6 +96,7 @@ func (q *Queries) CreateWiki(ctx context.Context, arg CreateWikiParams) (Wiki, e
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
@@ -151,7 +154,7 @@ func (q *Queries) DeleteWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID)
|
||||
}
|
||||
|
||||
const getWikiById = `-- name: GetWikiById :one
|
||||
SELECT id, project_id, title, content, is_deleted, created_at, updated_at
|
||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
||||
FROM wikis
|
||||
WHERE id = $1 AND is_deleted = false
|
||||
`
|
||||
@@ -163,6 +166,29 @@ func (q *Queries) GetWikiById(ctx context.Context, id pgtype.UUID) (Wiki, error)
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWikiBySlug = `-- name: GetWikiBySlug :one
|
||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
||||
FROM wikis
|
||||
WHERE slug = $1 AND is_deleted = false
|
||||
`
|
||||
|
||||
func (q *Queries) GetWikiBySlug(ctx context.Context, slug pgtype.Text) (Wiki, error) {
|
||||
row := q.db.QueryRow(ctx, getWikiBySlug, slug)
|
||||
var i Wiki
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
@@ -172,7 +198,7 @@ func (q *Queries) GetWikiById(ctx context.Context, id pgtype.UUID) (Wiki, error)
|
||||
}
|
||||
|
||||
const getWikisByIDs = `-- name: GetWikisByIDs :many
|
||||
SELECT id, project_id, title, content, is_deleted, created_at, updated_at FROM wikis WHERE id = ANY($1::uuid[]) AND is_deleted = false
|
||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at FROM wikis WHERE id = ANY($1::uuid[]) AND is_deleted = false
|
||||
`
|
||||
|
||||
func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]Wiki, error) {
|
||||
@@ -188,6 +214,7 @@ func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
@@ -204,7 +231,7 @@ func (q *Queries) GetWikisByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]
|
||||
}
|
||||
|
||||
const getWikisByProjectId = `-- name: GetWikisByProjectId :many
|
||||
SELECT id, project_id, title, content, is_deleted, created_at, updated_at
|
||||
SELECT id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
||||
FROM wikis
|
||||
WHERE project_id = $1 AND is_deleted = false
|
||||
`
|
||||
@@ -222,6 +249,7 @@ func (q *Queries) GetWikisByProjectId(ctx context.Context, projectID pgtype.UUID
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
@@ -238,7 +266,7 @@ func (q *Queries) GetWikisByProjectId(ctx context.Context, projectID pgtype.UUID
|
||||
}
|
||||
|
||||
const searchWikis = `-- name: SearchWikis :many
|
||||
SELECT w.id, w.project_id, w.title, w.content, w.is_deleted, w.created_at, w.updated_at
|
||||
SELECT w.id, w.project_id, w.title, w.slug, w.content, w.is_deleted, w.created_at, w.updated_at
|
||||
FROM wikis w
|
||||
WHERE w.is_deleted = false
|
||||
AND ($1::uuid IS NULL OR w.project_id = $1::uuid)
|
||||
@@ -285,6 +313,7 @@ func (q *Queries) SearchWikis(ctx context.Context, arg SearchWikisParams) ([]Wik
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
@@ -304,14 +333,16 @@ const updateWiki = `-- name: UpdateWiki :one
|
||||
UPDATE wikis
|
||||
SET
|
||||
title = COALESCE($1, title),
|
||||
content = COALESCE($2, content),
|
||||
project_id = COALESCE($3, project_id)
|
||||
WHERE id = $4 AND is_deleted = false
|
||||
RETURNING id, project_id, title, content, is_deleted, created_at, updated_at
|
||||
slug = COALESCE($2, slug),
|
||||
content = COALESCE($3, content),
|
||||
project_id = COALESCE($4, project_id)
|
||||
WHERE id = $5 AND is_deleted = false
|
||||
RETURNING id, project_id, title, slug, content, is_deleted, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateWikiParams struct {
|
||||
Title pgtype.Text `json:"title"`
|
||||
Slug pgtype.Text `json:"slug"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
ProjectID pgtype.UUID `json:"project_id"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
@@ -320,6 +351,7 @@ type UpdateWikiParams struct {
|
||||
func (q *Queries) UpdateWiki(ctx context.Context, arg UpdateWikiParams) (Wiki, error) {
|
||||
row := q.db.QueryRow(ctx, updateWiki,
|
||||
arg.Title,
|
||||
arg.Slug,
|
||||
arg.Content,
|
||||
arg.ProjectID,
|
||||
arg.ID,
|
||||
@@ -329,6 +361,7 @@ func (q *Queries) UpdateWiki(ctx context.Context, arg UpdateWikiParams) (Wiki, e
|
||||
&i.ID,
|
||||
&i.ProjectID,
|
||||
&i.Title,
|
||||
&i.Slug,
|
||||
&i.Content,
|
||||
&i.IsDeleted,
|
||||
&i.CreatedAt,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
type WikiEntity struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Slug string `json:"slug"`
|
||||
Content string `json:"content"`
|
||||
ProjectID string `json:"project_id"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
@@ -22,6 +23,7 @@ func (w *WikiEntity) ToResponse() *response.WikiResponse {
|
||||
return &response.WikiResponse{
|
||||
ID: w.ID,
|
||||
Title: w.Title,
|
||||
Slug: w.Slug,
|
||||
Content: w.Content,
|
||||
ProjectID: w.ProjectID,
|
||||
IsDeleted: w.IsDeleted,
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
type EntityRepository interface {
|
||||
GetByID(ctx context.Context, id pgtype.UUID) (*models.EntityEntity, error)
|
||||
GetByIDs(ctx context.Context, ids []string) ([]*models.EntityEntity, error)
|
||||
GetBySlug(ctx context.Context, slug string) (*models.EntityEntity, error)
|
||||
Search(ctx context.Context, params sqlc.SearchEntitiesParams) ([]*models.EntityEntity, error)
|
||||
Create(ctx context.Context, params sqlc.CreateEntityParams) (*models.EntityEntity, error)
|
||||
Update(ctx context.Context, params sqlc.UpdateEntityParams) (*models.EntityEntity, error)
|
||||
@@ -83,17 +84,17 @@ func (r *entityRepository) getByIDsWithFallback(ctx context.Context, ids []strin
|
||||
if err == nil {
|
||||
for _, row := range dbRows {
|
||||
item := models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
dbMap[item.ID] = &item
|
||||
}
|
||||
@@ -140,17 +141,17 @@ func (r *entityRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models
|
||||
}
|
||||
|
||||
entity = models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Set(ctx, cacheId, entity, constants.NormalCacheDuration)
|
||||
|
||||
@@ -174,17 +175,17 @@ func (r *entityRepository) Search(ctx context.Context, params sqlc.SearchEntitie
|
||||
|
||||
for _, row := range rows {
|
||||
entity := &models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
ids = append(ids, entity.ID)
|
||||
entities = append(entities, entity)
|
||||
@@ -209,17 +210,17 @@ func (r *entityRepository) Create(ctx context.Context, params sqlc.CreateEntityP
|
||||
}
|
||||
|
||||
entity := models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
|
||||
return &entity, nil
|
||||
@@ -231,19 +232,20 @@ func (r *entityRepository) Update(ctx context.Context, params sqlc.UpdateEntityP
|
||||
return nil, err
|
||||
}
|
||||
entity := models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("entity:id:%s", entity.ID))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("entity:slug:%s", entity.Slug))
|
||||
return &entity, nil
|
||||
}
|
||||
|
||||
@@ -274,17 +276,17 @@ func (r *entityRepository) GetByProjectID(ctx context.Context, projectID pgtype.
|
||||
|
||||
for _, row := range rows {
|
||||
entity := &models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
ids = append(ids, entity.ID)
|
||||
entities = append(entities, entity)
|
||||
@@ -315,3 +317,35 @@ func (r *entityRepository) DeleteByIDs(ctx context.Context, ids []pgtype.UUID) e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *entityRepository) GetBySlug(ctx context.Context, slug string) (*models.EntityEntity, error) {
|
||||
cacheKey := fmt.Sprintf("entity:slug:%s", slug)
|
||||
var entity models.EntityEntity
|
||||
err := r.c.Get(ctx, cacheKey, &entity)
|
||||
if err == nil {
|
||||
_ = r.c.Set(ctx, cacheKey, entity, constants.NormalCacheDuration)
|
||||
return &entity, nil
|
||||
}
|
||||
|
||||
row, err := r.q.GetEntityBySlug(ctx, convert.StringToText(slug))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entity = models.EntityEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Name: row.Name,
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Description: convert.TextToString(row.Description),
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
Status: convert.Int2ToInt16Ptr(row.Status),
|
||||
TimeStart: convert.Int4ToPtr(row.TimeStart),
|
||||
TimeEnd: convert.Int4ToPtr(row.TimeEnd),
|
||||
IsDeleted: row.IsDeleted,
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Set(ctx, cacheKey, entity, constants.NormalCacheDuration)
|
||||
|
||||
return &entity, nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
type WikiRepository interface {
|
||||
GetByID(ctx context.Context, id pgtype.UUID) (*models.WikiEntity, error)
|
||||
GetByIDs(ctx context.Context, ids []string) ([]*models.WikiEntity, error)
|
||||
GetBySlug(ctx context.Context, slug string) (*models.WikiEntity, error)
|
||||
Search(ctx context.Context, params sqlc.SearchWikisParams) ([]*models.WikiEntity, error)
|
||||
Create(ctx context.Context, params sqlc.CreateWikiParams) (*models.WikiEntity, error)
|
||||
Update(ctx context.Context, params sqlc.UpdateWikiParams) (*models.WikiEntity, error)
|
||||
@@ -90,6 +91,7 @@ func (r *wikiRepository) getByIDsWithFallback(ctx context.Context, ids []string)
|
||||
item := models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
@@ -143,8 +145,10 @@ func (r *wikiRepository) GetByID(ctx context.Context, id pgtype.UUID) (*models.W
|
||||
wiki = models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
@@ -172,8 +176,10 @@ func (r *wikiRepository) Search(ctx context.Context, params sqlc.SearchWikisPara
|
||||
wiki := &models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
@@ -201,8 +207,10 @@ func (r *wikiRepository) Create(ctx context.Context, params sqlc.CreateWikiParam
|
||||
wiki := models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
@@ -218,12 +226,15 @@ func (r *wikiRepository) Update(ctx context.Context, params sqlc.UpdateWikiParam
|
||||
wiki := models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("wiki:id:%s", wiki.ID))
|
||||
_ = r.c.Del(ctx, fmt.Sprintf("wiki:slug:%s", wiki.Slug))
|
||||
return &wiki, nil
|
||||
}
|
||||
|
||||
@@ -272,6 +283,7 @@ func (r *wikiRepository) GetByProjectID(ctx context.Context, projectID pgtype.UU
|
||||
wiki := &models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
@@ -318,3 +330,32 @@ func (r *wikiRepository) DeleteEntityWiki(ctx context.Context, entityID pgtype.U
|
||||
WikiID: wikiID,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *wikiRepository) GetBySlug(ctx context.Context, slug string) (*models.WikiEntity, error) {
|
||||
cacheKey := fmt.Sprintf("wiki:slug:%s", slug)
|
||||
var wiki models.WikiEntity
|
||||
err := r.c.Get(ctx, cacheKey, &wiki)
|
||||
if err == nil {
|
||||
_ = r.c.Set(ctx, cacheKey, wiki, constants.NormalCacheDuration)
|
||||
return &wiki, nil
|
||||
}
|
||||
|
||||
row, err := r.q.GetWikiBySlug(ctx, convert.StringToText(slug))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wiki = models.WikiEntity{
|
||||
ID: convert.UUIDToString(row.ID),
|
||||
Title: convert.TextToString(row.Title),
|
||||
Slug: convert.TextToString(row.Slug),
|
||||
Content: convert.TextToString(row.Content),
|
||||
IsDeleted: row.IsDeleted,
|
||||
ProjectID: convert.UUIDToString(row.ProjectID),
|
||||
CreatedAt: convert.TimeToPtr(row.CreatedAt),
|
||||
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
|
||||
}
|
||||
_ = r.c.Set(ctx, cacheKey, wiki, constants.NormalCacheDuration)
|
||||
|
||||
return &wiki, nil
|
||||
}
|
||||
|
||||
@@ -9,5 +9,8 @@ import (
|
||||
func EntityRoutes(router fiber.Router, entityController *controllers.EntityController) {
|
||||
entity := router.Group("/entities")
|
||||
entity.Get("/", entityController.SearchEntities)
|
||||
entity.Get("/slug/exists", entityController.IsExistEntitySlug)
|
||||
entity.Get("/slug/:slug", entityController.GetEntityBySlug)
|
||||
entity.Get("/:id", entityController.GetEntityById)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,5 +9,8 @@ import (
|
||||
func WikiRoutes(router fiber.Router, wikiController *controllers.WikiController) {
|
||||
wiki := router.Group("/wikis")
|
||||
wiki.Get("/", wikiController.SearchWikis)
|
||||
wiki.Get("/slug/exists", wikiController.IsExistWikiSlug)
|
||||
wiki.Get("/slug/:slug", wikiController.GetWikiBySlug)
|
||||
wiki.Get("/:id", wikiController.GetWikiById)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
@@ -14,6 +16,8 @@ import (
|
||||
|
||||
type EntityService interface {
|
||||
GetEntityByID(ctx context.Context, id string) (*response.EntityResponse, *fiber.Error)
|
||||
GetEntityBySlug(ctx context.Context, slug string) (*response.EntityResponse, *fiber.Error)
|
||||
IsExistEntitySlug(ctx context.Context, slug string) (bool, *fiber.Error)
|
||||
SearchEntities(ctx context.Context, req *request.SearchEntityDto) ([]*response.EntityResponse, *fiber.Error)
|
||||
}
|
||||
|
||||
@@ -40,6 +44,29 @@ func (s *entityService) GetEntityByID(ctx context.Context, id string) (*response
|
||||
return entity.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *entityService) GetEntityBySlug(ctx context.Context, slug string) (*response.EntityResponse, *fiber.Error) {
|
||||
if slug == "" {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Slug is required")
|
||||
}
|
||||
entity, err := s.entityRepo.GetBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Entity not found")
|
||||
}
|
||||
|
||||
return entity.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *entityService) IsExistEntitySlug(ctx context.Context, slug string) (bool, *fiber.Error) {
|
||||
if slug == "" {
|
||||
return false, fiber.NewError(fiber.StatusBadRequest, "Slug is required")
|
||||
}
|
||||
entity, err := s.entityRepo.GetBySlug(ctx, slug)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return false, fiber.NewError(fiber.StatusInternalServerError, "Failed to check slug existence")
|
||||
}
|
||||
return entity != nil, nil
|
||||
}
|
||||
|
||||
func (s *entityService) SearchEntities(ctx context.Context, req *request.SearchEntityDto) ([]*response.EntityResponse, *fiber.Error) {
|
||||
limit := int32(25)
|
||||
if req.Limit > 0 {
|
||||
|
||||
@@ -2,7 +2,9 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
@@ -95,6 +97,35 @@ func (s *submissionService) CreateSubmission(ctx context.Context, userID string,
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Commit does not belong to project")
|
||||
}
|
||||
|
||||
var snapshotData request.CommitSnapshot
|
||||
if err := json.Unmarshal(commit.SnapshotJson, &snapshotData); err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to parse commit snapshot")
|
||||
}
|
||||
|
||||
for _, entity := range snapshotData.Entities {
|
||||
if entity.Slug != nil {
|
||||
exist, err := s.entityRepo.GetBySlug(ctx, *entity.Slug)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get entity")
|
||||
}
|
||||
if exist != nil {
|
||||
return nil, fiber.NewError(fiber.StatusConflict, fmt.Sprintf("Entity %s already exists", *entity.Slug))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, wiki := range snapshotData.Wikis {
|
||||
if wiki.Slug != nil {
|
||||
exist, err := s.wikiRepo.GetBySlug(ctx, *wiki.Slug)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to get wiki")
|
||||
}
|
||||
if exist != nil {
|
||||
return nil, fiber.NewError(fiber.StatusConflict, fmt.Sprintf("Wiki %s already exists", *wiki.Slug))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project, err := s.projectRepo.GetByID(ctx, projectUUID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project not found")
|
||||
@@ -178,12 +209,12 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
listDeleteWikis := make([]pgtype.UUID, 0)
|
||||
listDeleteGeometries := make([]pgtype.UUID, 0)
|
||||
var snapshotData request.CommitSnapshot
|
||||
if status == constants.StatusTypeApproved {
|
||||
err = json.Unmarshal(commit.SnapshotJson, &snapshotData)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to parse commit snapshot")
|
||||
}
|
||||
err = json.Unmarshal(commit.SnapshotJson, &snapshotData)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to parse commit snapshot")
|
||||
}
|
||||
|
||||
if status == constants.StatusTypeApproved {
|
||||
projectUUID, err := convert.StringToUUID(commit.ProjectID)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project ID")
|
||||
@@ -312,7 +343,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update entity: "+entity.ID)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update entity: "+err.Error())
|
||||
}
|
||||
|
||||
newEntities = append(newEntities, snapshotData.Entities[i])
|
||||
@@ -330,7 +361,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create entity: "+entity.ID)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create entity: "+err.Error())
|
||||
}
|
||||
|
||||
newEntities = append(newEntities, snapshotData.Entities[i])
|
||||
@@ -390,7 +421,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
|
||||
_, err := geometryRepo.Update(ctx, params)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update geometry: "+geo.ID)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update geometry: "+err.Error())
|
||||
}
|
||||
newGeometries = append(newGeometries, snapshotData.Geometries[i])
|
||||
|
||||
@@ -413,7 +444,7 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
|
||||
_, err := geometryRepo.Create(ctx, params)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create geometry: "+geo.ID)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create geometry: "+err.Error())
|
||||
}
|
||||
newGeometries = append(newGeometries, snapshotData.Geometries[i])
|
||||
|
||||
@@ -453,11 +484,12 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
_, err := wikiRepo.Update(ctx, sqlc.UpdateWikiParams{
|
||||
ID: wikiUUID,
|
||||
Title: convert.StringToText(wiki.Title),
|
||||
Slug: convert.PtrToText(wiki.Slug),
|
||||
Content: convert.StringToText(wiki.Doc),
|
||||
ProjectID: projectUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update wiki: "+wiki.ID)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to update wiki: "+err.Error())
|
||||
}
|
||||
newWikis = append(newWikis, snapshotData.Wikis[i])
|
||||
|
||||
@@ -465,11 +497,12 @@ func (s *submissionService) UpdateSubmissionStatus(ctx context.Context, reviewer
|
||||
_, err := wikiRepo.Create(ctx, sqlc.CreateWikiParams{
|
||||
ID: wikiUUID,
|
||||
Title: convert.StringToText(wiki.Title),
|
||||
Slug: convert.PtrToText(wiki.Slug),
|
||||
Content: convert.StringToText(wiki.Doc),
|
||||
ProjectID: projectUUID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki: "+wiki.ID)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to create wiki: "+err.Error())
|
||||
}
|
||||
newWikis = append(newWikis, snapshotData.Wikis[i])
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"history-api/internal/dtos/request"
|
||||
"history-api/internal/dtos/response"
|
||||
"history-api/internal/gen/sqlc"
|
||||
@@ -14,6 +16,8 @@ import (
|
||||
|
||||
type WikiService interface {
|
||||
GetWikiByID(ctx context.Context, id string) (*response.WikiResponse, *fiber.Error)
|
||||
GetWikiBySlug(ctx context.Context, slug string) (*response.WikiResponse, *fiber.Error)
|
||||
IsExistWikiSlug(ctx context.Context, slug string) (bool, *fiber.Error)
|
||||
SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, *fiber.Error)
|
||||
}
|
||||
|
||||
@@ -40,6 +44,29 @@ func (s *wikiService) GetWikiByID(ctx context.Context, id string) (*response.Wik
|
||||
return wiki.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *wikiService) GetWikiBySlug(ctx context.Context, slug string) (*response.WikiResponse, *fiber.Error) {
|
||||
if slug == "" {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Slug is required")
|
||||
}
|
||||
wiki, err := s.wikiRepo.GetBySlug(ctx, slug)
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Wiki not found")
|
||||
}
|
||||
|
||||
return wiki.ToResponse(), nil
|
||||
}
|
||||
|
||||
func (s *wikiService) IsExistWikiSlug(ctx context.Context, slug string) (bool, *fiber.Error) {
|
||||
if slug == "" {
|
||||
return false, fiber.NewError(fiber.StatusBadRequest, "Slug is required")
|
||||
}
|
||||
wiki, err := s.wikiRepo.GetBySlug(ctx, slug)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return false, fiber.NewError(fiber.StatusInternalServerError, "Failed to check slug existence")
|
||||
}
|
||||
return wiki != nil, nil
|
||||
}
|
||||
|
||||
func (s *wikiService) SearchWikis(ctx context.Context, req *request.SearchWikiDto) ([]*response.WikiResponse, *fiber.Error) {
|
||||
limit := int32(25)
|
||||
if req.Limit > 0 {
|
||||
|
||||
@@ -17,6 +17,7 @@ var (
|
||||
EMAIL_REGEX = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
|
||||
YOUTUBE_VIDEO_ID_REGEX = regexp.MustCompile(`(?:\/|v=|\/v\/|embed\/|watch\?v=|watch\?.+&v=)([\w-]{11})`)
|
||||
BANK_INPUT = regexp.MustCompile(`[__]{2,}`)
|
||||
SLUG_REGEX = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)
|
||||
)
|
||||
|
||||
func ValidatePassword(password string) error {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"history-api/pkg/constants"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/google/uuid"
|
||||
@@ -53,6 +55,14 @@ func init() {
|
||||
}
|
||||
return u.Version() == 7
|
||||
})
|
||||
|
||||
validate.RegisterValidation("slug", func(fl validator.FieldLevel) bool {
|
||||
val := fl.Field().String()
|
||||
if val == "" {
|
||||
return true
|
||||
}
|
||||
return constants.SLUG_REGEX.MatchString(val)
|
||||
})
|
||||
}
|
||||
|
||||
func isValidURL(s string) bool {
|
||||
|
||||
Reference in New Issue
Block a user