commit a88bd957e306e7bb4f541a8ac67981ee8e85b64a Author: hanif salafi Date: Fri Sep 19 11:08:42 2025 +0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff92df --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor +debug.log +/.ideadebug.log diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..fee9d6c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,33 @@ +stages: + - build-image + - deploy + +#build-1: +# stage: build-app +# image: golang:alpine +# script: +# - go build -o main . +# artifacts: +# paths: +# - main + +build-2: + stage: build-image + image: docker/compose:latest + services: + - name: docker:dind + command: [ "--insecure-registry=103.82.242.92:8900" ] + script: + - docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 103.82.242.92:8900 + - docker-compose build + - docker tag registry.gitlab.com/hanifsalafi/narasi-ahli-be:dev 103.82.242.92:8900/medols/narasi-ahli-be:dev + - docker push 103.82.242.92:8900/medols/narasi-ahli-be:dev + +deploy: + stage: deploy + when: on_success + image: curlimages/curl:latest + services: + - docker:dind + script: + - curl --user $JENKINS_USER:$JENKINS_PWD http://38.47.180.165:8080/job/autodeploy-medols-be/build?token=autodeploymedols \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..5c16dd2 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/narasi-ahli-be.iml b/.idea/narasi-ahli-be.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/narasi-ahli-be.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..06753a7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:alpine + +WORKDIR /app +COPY . . + +RUN go mod download +RUN go get -v ./... +RUN go mod vendor +RUN go build -o main . + +EXPOSE 8800 + +CMD ["sh", "-c", "go run main.go"] \ No newline at end of file diff --git a/app/database/entity/activity_log_types.entity.go b/app/database/entity/activity_log_types.entity.go new file mode 100644 index 0000000..d7d3ae0 --- /dev/null +++ b/app/database/entity/activity_log_types.entity.go @@ -0,0 +1,7 @@ +package entity + +type ActivityLogTypes struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool"` +} diff --git a/app/database/entity/activity_logs.entity.go b/app/database/entity/activity_logs.entity.go new file mode 100644 index 0000000..1e7b7a8 --- /dev/null +++ b/app/database/entity/activity_logs.entity.go @@ -0,0 +1,15 @@ +package entity + +import ( + "time" +) + +type ActivityLogs struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ActivityTypeId int `json:"activity_type_id" gorm:"type:int4"` + Url string `json:"url" gorm:"type:varchar"` + VisitorIp *string `json:"visitor_ip" gorm:"type:varchar"` + ArticleId *uint `json:"article_id" gorm:"type:int4"` + UserId *uint `json:"user_id" gorm:"type:int4"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` +} diff --git a/app/database/entity/advertisement.entity.go b/app/database/entity/advertisement.entity.go new file mode 100644 index 0000000..4dcc541 --- /dev/null +++ b/app/database/entity/advertisement.entity.go @@ -0,0 +1,20 @@ +package entity + +import ( + "time" +) + +type Advertisement struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Title string `json:"title" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + RedirectLink string `json:"redirect_link" gorm:"type:varchar"` + ContentFilePath *string `json:"content_file_path" gorm:"type:varchar"` + ContentFileName *string `json:"content_file_name" gorm:"type:varchar"` + Placement string `json:"placement" gorm:"type:varchar"` + StatusId int `json:"status_id" gorm:"type:int4"` + IsPublish bool `json:"is_publish" gorm:"type:bool"` + IsActive bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/ai_chat_logs.entity.go b/app/database/entity/ai_chat_logs.entity.go new file mode 100644 index 0000000..605f418 --- /dev/null +++ b/app/database/entity/ai_chat_logs.entity.go @@ -0,0 +1,19 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type AIChatLogs struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + SessionID uint `json:"session_id" gorm:"type:int4;not null;index"` + UserID uint `json:"user_id" gorm:"type:int4;not null;index"` + StartDate time.Time `json:"start_date" gorm:"not null"` + EndDate *time.Time `json:"end_date"` + TotalDuration int64 `json:"total_duration" gorm:"type:bigint;default:0"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + Session *AIChatSessions `json:"session" gorm:"foreignKey:SessionID;references:ID"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} diff --git a/app/database/entity/ai_chat_messages.entity.go b/app/database/entity/ai_chat_messages.entity.go new file mode 100644 index 0000000..414dc77 --- /dev/null +++ b/app/database/entity/ai_chat_messages.entity.go @@ -0,0 +1,14 @@ +package entity + +import ( + "time" +) + +type AIChatMessages struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + SessionID uint `json:"session_id" gorm:"type:int4;not null;index"` + MessageType string `json:"message_type" gorm:"type:varchar;not null"` + Content string `json:"content" gorm:"type:text;not null"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + Session *AIChatSessions `json:"session" gorm:"foreignKey:SessionID;references:ID"` +} diff --git a/app/database/entity/ai_chat_sessions.entity.go b/app/database/entity/ai_chat_sessions.entity.go new file mode 100644 index 0000000..8250f1e --- /dev/null +++ b/app/database/entity/ai_chat_sessions.entity.go @@ -0,0 +1,19 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type AIChatSessions struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + AISessionID string `json:"ai_session_id" gorm:"type:varchar;not null;unique"` + UserID uint `json:"user_id" gorm:"type:int4;not null;index"` + AgentID *string `json:"agent_id" gorm:"type:varchar"` + Title string `json:"title" gorm:"type:varchar;not null"` + MessageCount int `json:"message_count" gorm:"type:int4;default:0"` + Status string `json:"status" gorm:"type:varchar;default:'active'"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} diff --git a/app/database/entity/article_approvals.entity.go b/app/database/entity/article_approvals.entity.go new file mode 100644 index 0000000..e321bcc --- /dev/null +++ b/app/database/entity/article_approvals.entity.go @@ -0,0 +1,15 @@ +package entity + +import ( + "time" +) + +type ArticleApprovals struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ArticleId uint `json:"article_id" gorm:"type:int4"` + ApprovalBy uint `json:"approval_by" gorm:"type:int4"` + StatusId int `json:"status_id" gorm:"type:int4"` + Message string `json:"message" gorm:"type:varchar"` + ApprovalAtLevel *int `json:"approval_at_level" gorm:"type:int4"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` +} diff --git a/app/database/entity/article_categories.entity.go b/app/database/entity/article_categories.entity.go new file mode 100644 index 0000000..6eb890d --- /dev/null +++ b/app/database/entity/article_categories.entity.go @@ -0,0 +1,24 @@ +package entity + +import ( + "time" +) + +type ArticleCategories struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Title string `json:"title" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"` + Slug *string `json:"slug" gorm:"type:varchar"` + ParentId *int `json:"parent_id" gorm:"type:int4"` + Tags *string `json:"tags" gorm:"type:varchar"` + Position *int `json:"position" gorm:"type:int4"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + OldCategoryId *uint `json:"old_category_id" gorm:"type:int4"` + StatusId int `json:"status_id" gorm:"type:int4;default:1"` + IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` + PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/article_category_details/article_category_details.entity.go b/app/database/entity/article_category_details/article_category_details.entity.go new file mode 100644 index 0000000..ad9018d --- /dev/null +++ b/app/database/entity/article_category_details/article_category_details.entity.go @@ -0,0 +1,16 @@ +package article_category_details + +import ( + entity "narasi-ahli-be/app/database/entity" + "time" +) + +type ArticleCategoryDetails struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ArticleId uint `json:"article_id" gorm:"type:int4"` + CategoryId int `json:"category_id" gorm:"type:int4"` + Category *entity.ArticleCategories `json:"category" gorm:"foreignKey:CategoryId;references:ID"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/article_comments.entity.go b/app/database/entity/article_comments.entity.go new file mode 100644 index 0000000..68eb427 --- /dev/null +++ b/app/database/entity/article_comments.entity.go @@ -0,0 +1,21 @@ +package entity + +import ( + "time" +) + +type ArticleComments struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Message string `json:"message" gorm:"type:varchar"` + ArticleId uint `json:"article_id" gorm:"type:int4"` + CommentFrom *uint `json:"comment_from" gorm:"type:int4"` + ParentId *int `json:"parent_id" gorm:"type:int4"` + IsPublic bool `json:"is_public" gorm:"type:bool;default:false"` + StatusId int `json:"status_id" gorm:"type:int4;default:0"` + ApprovedAt *time.Time `json:"approved_at" gorm:"type:timestamp"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} + +// statusId => 0: waiting, 1: accepted, 2: replied, 3: rejected diff --git a/app/database/entity/article_files.entity.go b/app/database/entity/article_files.entity.go new file mode 100644 index 0000000..7317069 --- /dev/null +++ b/app/database/entity/article_files.entity.go @@ -0,0 +1,27 @@ +package entity + +import ( + "time" +) + +type ArticleFiles struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ArticleId uint `json:"article_id" gorm:"type:int4"` + UploadID *string `json:"upload_id" gorm:"type:varchar"` + FilePath *string `json:"file_path" gorm:"type:varchar"` + FileUrl *string `json:"file_url" gorm:"type:varchar"` + FileName *string `json:"file_name" gorm:"type:varchar"` + FileThumbnail *string `json:"file_thumbnail" gorm:"type:varchar"` + FileAlt *string `json:"file_alt" gorm:"type:varchar"` + WidthPixel *string `json:"width_pixel" gorm:"type:varchar"` + HeightPixel *string `json:"height_pixel" gorm:"type:varchar"` + Size *string `json:"size" gorm:"type:varchar"` + DownloadCount *int `json:"download_count" gorm:"type:int4;default:0"` + CreatedById int `json:"created_by_id" gorm:"type:int4"` + StatusId int `json:"status_id" gorm:"type:int4"` + IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` + PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` + IsActive bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/articles.entity.go b/app/database/entity/articles.entity.go new file mode 100644 index 0000000..c316667 --- /dev/null +++ b/app/database/entity/articles.entity.go @@ -0,0 +1,37 @@ +package entity + +import ( + "time" +) + +type Articles struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Title string `json:"title" gorm:"type:varchar"` + Slug string `json:"slug" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + CategoryId int `json:"category_id" gorm:"type:int4"` + HtmlDescription string `json:"html_description" gorm:"type:varchar"` + TypeId int `json:"type_id" gorm:"type:int4"` + Tags string `json:"tags" gorm:"type:varchar"` + ThumbnailName *string `json:"thumbnail_name" gorm:"type:varchar"` + ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"` + PageUrl *string `json:"page_url" gorm:"type:varchar"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + AiArticleId *int `json:"ai_article_id" gorm:"type:int4"` + CommentCount *int `json:"comment_count" gorm:"type:int4;default:0"` + ShareCount *int `json:"share_count" gorm:"type:int4;default:0"` + ViewCount *int `json:"view_count" gorm:"type:int4;default:0"` + StatusId *int `json:"status_id" gorm:"type:int4"` + OldId *uint `json:"old_id" gorm:"type:int4"` + NeedApprovalFrom *int `json:"need_approval_from" gorm:"type:int4"` + HasApprovedBy *string `json:"has_approved_by" gorm:"type:varchar"` + IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` + IsBanner *bool `json:"is_banner" gorm:"type:bool;default:false"` + PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` + IsDraft *bool `json:"is_draft" gorm:"type:bool;default:false"` + DraftedAt *time.Time `json:"drafted_at" gorm:"type:timestamp"` + PublishSchedule *string `json:"publish_schedule" gorm:"type:varchar"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/audit_trails.entity.go b/app/database/entity/audit_trails.entity.go new file mode 100644 index 0000000..b93f3ee --- /dev/null +++ b/app/database/entity/audit_trails.entity.go @@ -0,0 +1,19 @@ +package entity + +import ( + "time" +) + +type AuditTrails struct { + ID uint `gorm:"primaryKey"` + Method string + Path string + IP string + Status int + UserID *string + RequestHeaders string + RequestBody string + ResponseBody string + DurationMs int64 + CreatedAt time.Time +} diff --git a/app/database/entity/chat_messages.entity.go b/app/database/entity/chat_messages.entity.go new file mode 100644 index 0000000..005a81e --- /dev/null +++ b/app/database/entity/chat_messages.entity.go @@ -0,0 +1,21 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type ChatMessages struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ConversationID uint `json:"conversation_id" gorm:"type:int4;not null;index"` + SenderID uint `json:"sender_id" gorm:"type:int4;not null;index"` + MessageText *string `json:"message_text" gorm:"type:text"` + MessageType string `json:"message_type" gorm:"type:varchar;not null;default:'text'"` + FileURL *string `json:"file_url" gorm:"type:varchar"` + FileName *string `json:"file_name" gorm:"type:varchar"` + FileSize *int64 `json:"file_size" gorm:"type:bigint"` + IsRead bool `json:"is_read" gorm:"default:false"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + Conversation *Conversations `json:"conversation" gorm:"foreignKey:ConversationID;references:ID"` + Sender *users.Users `json:"sender" gorm:"foreignKey:SenderID;references:ID"` +} diff --git a/app/database/entity/cities.entity.go b/app/database/entity/cities.entity.go new file mode 100644 index 0000000..bdb0a80 --- /dev/null +++ b/app/database/entity/cities.entity.go @@ -0,0 +1,7 @@ +package entity + +type Cities struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + CityName string `json:"city_name" gorm:"type:varchar"` + ProvId int `json:"prov_id" gorm:"type:int4"` +} diff --git a/app/database/entity/conversations.entity.go b/app/database/entity/conversations.entity.go new file mode 100644 index 0000000..67218c4 --- /dev/null +++ b/app/database/entity/conversations.entity.go @@ -0,0 +1,17 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type Conversations struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Participant1ID uint `json:"participant1_id" gorm:"type:int4;not null;index"` + Participant2ID uint `json:"participant2_id" gorm:"type:int4;not null;index"` + LastMessageAt *time.Time `json:"last_message_at"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + Participant1 *users.Users `json:"participant1" gorm:"foreignKey:Participant1ID;references:ID"` + Participant2 *users.Users `json:"participant2" gorm:"foreignKey:Participant2ID;references:ID"` +} diff --git a/app/database/entity/csrf_token_records.entity.go b/app/database/entity/csrf_token_records.entity.go new file mode 100644 index 0000000..fee0a14 --- /dev/null +++ b/app/database/entity/csrf_token_records.entity.go @@ -0,0 +1,13 @@ +package entity + +import ( + "time" +) + +type CsrfTokenRecords struct { + ID uint `gorm:"primaryKey"` + Token string `gorm:"uniqueIndex;size:255"` + Value []byte `gorm:"value"` + ExpireAt time.Time `gorm:"index"` + CreatedAt time.Time +} diff --git a/app/database/entity/custom_static_pages.entity.go b/app/database/entity/custom_static_pages.entity.go new file mode 100644 index 0000000..95b86f8 --- /dev/null +++ b/app/database/entity/custom_static_pages.entity.go @@ -0,0 +1,16 @@ +package entity + +import ( + "time" +) + +type CustomStaticPages struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Title string `json:"title" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + Slug string `json:"slug" gorm:"type:varchar"` + HtmlBody string `json:"html_body" gorm:"type:text"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/districts.entity.go b/app/database/entity/districts.entity.go new file mode 100644 index 0000000..480de99 --- /dev/null +++ b/app/database/entity/districts.entity.go @@ -0,0 +1,7 @@ +package entity + +type Districts struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + DisName string `json:"dis_name" gorm:"type:varchar"` + CityId int `json:"city_id" gorm:"type:int4"` +} diff --git a/app/database/entity/education_history.entity.go b/app/database/entity/education_history.entity.go new file mode 100644 index 0000000..2ab90e4 --- /dev/null +++ b/app/database/entity/education_history.entity.go @@ -0,0 +1,19 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type EducationHistory struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null;index"` + SchoolName string `json:"school_name" gorm:"type:varchar;not null"` + Major string `json:"major" gorm:"type:varchar;not null"` + EducationLevel string `json:"education_level" gorm:"type:varchar;not null"` + GraduationYear int `json:"graduation_year" gorm:"type:int4;not null"` + CertificateImage *string `json:"certificate_image" gorm:"type:varchar"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} diff --git a/app/database/entity/feedbacks.entity.go b/app/database/entity/feedbacks.entity.go new file mode 100644 index 0000000..c735a7f --- /dev/null +++ b/app/database/entity/feedbacks.entity.go @@ -0,0 +1,20 @@ +package entity + +import ( + "time" +) + +type Feedbacks struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Message string `json:"message" gorm:"type:varchar"` + CommentFromName string `json:"comment_from_name" gorm:"type:varchar"` + CommentFromEmail string `json:"comment_from_email" gorm:"type:varchar"` + StatusId int `json:"status_id" gorm:"type:int4;default:0"` + ApprovedAt *time.Time `json:"approved_at" gorm:"type:timestamp"` + ReplyMessage *string `json:"reply_message" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} + +// statusId => 0: waiting, 1: accepted, 2: replied, 3: nothing diff --git a/app/database/entity/forgot_passwords.entity.go b/app/database/entity/forgot_passwords.entity.go new file mode 100644 index 0000000..368d0e5 --- /dev/null +++ b/app/database/entity/forgot_passwords.entity.go @@ -0,0 +1,14 @@ +package entity + +import ( + "time" +) + +type ForgotPasswords struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + KeycloakID string `json:"keycloak_id" gorm:"type:varchar"` + CodeRequest string `json:"code_request" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/magazine_files.entity.go b/app/database/entity/magazine_files.entity.go new file mode 100644 index 0000000..c16ad49 --- /dev/null +++ b/app/database/entity/magazine_files.entity.go @@ -0,0 +1,24 @@ +package entity + +import "time" + +type MagazineFiles struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Title string `json:"title" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + MagazineId uint `json:"magazine_id" gorm:"type:int4"` + DownloadCount *int `json:"download_count" gorm:"type:int4"` + StatusId int `json:"status_id" gorm:"type:int4"` + IsPublish *bool `json:"is_publish" gorm:"type:bool"` + FilePath *string `json:"file_path" gorm:"type:varchar"` + FileUrl *string `json:"file_url" gorm:"type:varchar"` + FileName *string `json:"file_name" gorm:"type:varchar"` + FileAlt *string `json:"file_alt" gorm:"type:varchar"` + WidthPixel *string `json:"width_pixel" gorm:"type:varchar"` + HeightPixel *string `json:"height_pixel" gorm:"type:varchar"` + Size *string `json:"size" gorm:"type:varchar"` + PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/magazines.entity.go b/app/database/entity/magazines.entity.go new file mode 100644 index 0000000..3cfc206 --- /dev/null +++ b/app/database/entity/magazines.entity.go @@ -0,0 +1,20 @@ +package entity + +import "time" + +type Magazines struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Title string `json:"title" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + ThumbnailName *string `json:"thumbnail_name" gorm:"type:varchar"` + ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"` + ThumbnailUrl *string `json:"thumbnail_url" gorm:"type:varchar"` + PageUrl *string `json:"page_url" gorm:"type:varchar"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + StatusId int `json:"status_id" gorm:"type:int4"` + IsPublish *bool `json:"is_publish" gorm:"type:bool"` + PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/master_approval_statuses.entity.go b/app/database/entity/master_approval_statuses.entity.go new file mode 100644 index 0000000..7a24565 --- /dev/null +++ b/app/database/entity/master_approval_statuses.entity.go @@ -0,0 +1,7 @@ +package entity + +type MasterApprovalStatuses struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool"` +} diff --git a/app/database/entity/master_menus.entity.go b/app/database/entity/master_menus.entity.go new file mode 100644 index 0000000..143e764 --- /dev/null +++ b/app/database/entity/master_menus.entity.go @@ -0,0 +1,18 @@ +package entity + +import "time" + +type MasterMenus struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + ModuleId int `json:"module_id" gorm:"type:int4"` + ParentMenuId *int `json:"parent_menu_id" gorm:"type:int4"` + Icon *string `json:"icon" gorm:"type:varchar"` + Group string `json:"group" gorm:"type:varchar"` + Position *int `json:"position" gorm:"type:int4"` + StatusId int `json:"status_id" gorm:"type:int4"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/master_modules.entity.go b/app/database/entity/master_modules.entity.go new file mode 100644 index 0000000..0d39445 --- /dev/null +++ b/app/database/entity/master_modules.entity.go @@ -0,0 +1,16 @@ +package entity + +import ( + "time" +) + +type MasterModules struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + PathUrl string `json:"path_url" gorm:"type:varchar"` + StatusId int `json:"status_id" gorm:"type:int4"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/master_statuses.entity.go b/app/database/entity/master_statuses.entity.go new file mode 100644 index 0000000..75d20a0 --- /dev/null +++ b/app/database/entity/master_statuses.entity.go @@ -0,0 +1,7 @@ +package entity + +type MasterStatuses struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool"` +} diff --git a/app/database/entity/one_time_passwords.entity.go b/app/database/entity/one_time_passwords.entity.go new file mode 100644 index 0000000..2475fa4 --- /dev/null +++ b/app/database/entity/one_time_passwords.entity.go @@ -0,0 +1,16 @@ +package entity + +import ( + "time" +) + +type OneTimePasswords struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Email string `json:"email" gorm:"type:varchar"` + Name *string `json:"name" gorm:"type:varchar"` + Identity *string `json:"identity" gorm:"type:varchar"` + OtpCode string `json:"otp_code" gorm:"type:varchar"` + ValidUntil time.Time `json:"valid_until" gorm:"default:(NOW() + INTERVAL '10 minutes')"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` +} diff --git a/app/database/entity/provinces.entity.go b/app/database/entity/provinces.entity.go new file mode 100644 index 0000000..f9f3902 --- /dev/null +++ b/app/database/entity/provinces.entity.go @@ -0,0 +1,9 @@ +package entity + +type Provinces struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ProvName string `json:"prov_name" gorm:"type:varchar"` + LocationId int `json:"location_id" gorm:"type:int4"` + Status int `json:"status" gorm:"type:int4"` + Timezone string `json:"timezone" gorm:"type:varchar"` +} diff --git a/app/database/entity/research_journals.entity.go b/app/database/entity/research_journals.entity.go new file mode 100644 index 0000000..71dd0c1 --- /dev/null +++ b/app/database/entity/research_journals.entity.go @@ -0,0 +1,18 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type ResearchJournals struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null;index"` + JournalTitle string `json:"journal_title" gorm:"type:varchar;not null"` + Publisher string `json:"publisher" gorm:"type:varchar;not null"` + JournalURL string `json:"journal_url" gorm:"type:varchar;not null"` + PublishedDate *time.Time `json:"published_date"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} diff --git a/app/database/entity/subscription.entity.go b/app/database/entity/subscription.entity.go new file mode 100644 index 0000000..710cbf4 --- /dev/null +++ b/app/database/entity/subscription.entity.go @@ -0,0 +1,14 @@ +package entity + +import ( + "time" + + ) + +type Subscription struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Email string `json:"email" gorm:"type:varchar"` + IsActive bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/user_levels/user_levels.entity.go b/app/database/entity/user_levels/user_levels.entity.go new file mode 100644 index 0000000..f95ddfa --- /dev/null +++ b/app/database/entity/user_levels/user_levels.entity.go @@ -0,0 +1,17 @@ +package user_levels + +import "time" + +type UserLevels struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + AliasName string `json:"alias_name" gorm:"type:varchar"` + LevelNumber int `json:"level_number" gorm:"type:int4"` + ParentLevelId *int `json:"parent_level_id" gorm:"type:int4"` + ProvinceId *int `json:"province_id" gorm:"type:int4"` + Group *string `json:"group" gorm:"type:varchar"` + IsApprovalActive *bool `json:"is_approval_active" gorm:"type:bool;default:false"` + IsActive *bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/user_levels/user_levels.entity.go~ b/app/database/entity/user_levels/user_levels.entity.go~ new file mode 100644 index 0000000..dbbea4d --- /dev/null +++ b/app/database/entity/user_levels/user_levels.entity.go~ @@ -0,0 +1,17 @@ +package entity + +import "time" + +type UserLevels struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + AliasName string `json:"alias_name" gorm:"type:varchar"` + LevelNumber int `json:"level_number" gorm:"type:int4"` + ParentLevelId *int `json:"parent_level_id" gorm:"type:int4"` + ProvinceId *int `json:"province_id" gorm:"type:int4"` + Group *string `json:"group" gorm:"type:varchar"` + IsApprovalActive *bool `json:"is_approval_active" gorm:"type:bool;default:false"` + IsActive *bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/user_role_accesses.entity.go b/app/database/entity/user_role_accesses.entity.go new file mode 100644 index 0000000..47d94df --- /dev/null +++ b/app/database/entity/user_role_accesses.entity.go @@ -0,0 +1,20 @@ +package entity + +import ( + "time" +) + +type UserRoleAccesses struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserRoleId uint `json:"user_role_id" gorm:"type:int4"` + MenuId int `json:"menu_id" gorm:"type:int4"` + IsViewEnabled bool `json:"is_view_enabled" gorm:"type:bool"` + IsInsertEnabled bool `json:"is_insert_enabled" gorm:"type:bool"` + IsUpdateEnabled bool `json:"is_update_enabled" gorm:"type:bool"` + IsDeleteEnabled bool `json:"is_delete_enabled" gorm:"type:bool"` + IsApprovalEnabled bool `json:"is_approval_enabled" gorm:"type:bool"` + IsAdminEnabled bool `json:"is_admin_enabled" gorm:"type:bool"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/user_role_level_details.entity.go b/app/database/entity/user_role_level_details.entity.go new file mode 100644 index 0000000..f187c06 --- /dev/null +++ b/app/database/entity/user_role_level_details.entity.go @@ -0,0 +1,15 @@ +package entity + +import ( + "time" + + ) + +type UserRoleLevelDetails struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserRoleId uint `json:"user_role_id" gorm:"type:int4"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/user_roles.entity.go b/app/database/entity/user_roles.entity.go new file mode 100644 index 0000000..8795923 --- /dev/null +++ b/app/database/entity/user_roles.entity.go @@ -0,0 +1,19 @@ +package entity + +import ( + "time" + + ) + +type UserRoles struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Name string `json:"name" gorm:"type:varchar"` + Description string `json:"description" gorm:"type:varchar"` + Code string `json:"code" gorm:"type:varchar"` + StatusId int `json:"status_id" gorm:"type:int4;default:1"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/users/users.entity.go b/app/database/entity/users/users.entity.go new file mode 100644 index 0000000..f5b0365 --- /dev/null +++ b/app/database/entity/users/users.entity.go @@ -0,0 +1,38 @@ +package users + +import ( + userLevels "narasi-ahli-be/app/database/entity/user_levels" + "time" +) + +type Users struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Username string `json:"username" gorm:"type:varchar"` + Email string `json:"email" gorm:"type:varchar"` + Fullname string `json:"fullname" gorm:"type:varchar"` + Address *string `json:"address" gorm:"type:varchar"` + PhoneNumber *string `json:"phone_number" gorm:"type:varchar"` + WorkType *string `json:"work_type" gorm:"type:varchar"` + GenderType *string `json:"gender_type" gorm:"type:varchar"` + IdentityType *string `json:"identity_type" gorm:"type:varchar"` + IdentityGroup *string `json:"identity_group" gorm:"type:varchar"` + IdentityGroupNumber *string `json:"identity_group_number" gorm:"type:varchar"` + IdentityNumber *string `json:"identity_number" gorm:"type:varchar"` + DateOfBirth *string `json:"date_of_birth" gorm:"type:varchar"` + LastEducation *string `json:"last_education" gorm:"type:varchar"` + Degree *string `json:"degree" gorm:"type:varchar"` + WhatsappNumber *string `json:"whatsapp_number" gorm:"type:varchar"` + LastJobTitle *string `json:"last_job_title" gorm:"type:varchar"` + UserRoleId uint `json:"user_role_id" gorm:"type:int4"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4"` + UserLevel *userLevels.UserLevels `json:"user_levels" gorm:"foreignKey:UserLevelId;references:ID"` + KeycloakId *string `json:"keycloak_id" gorm:"type:varchar"` + StatusId *int `json:"status_id" gorm:"type:int4;default:1"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"` + TempPassword *string `json:"temp_password" gorm:"type:varchar"` + IsEmailUpdated *bool `json:"is_email_updated" gorm:"type:bool;default:false"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/users/users.entity.go~ b/app/database/entity/users/users.entity.go~ new file mode 100644 index 0000000..9b5f57f --- /dev/null +++ b/app/database/entity/users/users.entity.go~ @@ -0,0 +1,38 @@ +package users + +import ( + userLevels "narasi-ahli-be/app/database/entity/user_levels" + "time" +) + +type Users struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Username string `json:"username" gorm:"type:varchar"` + Email string `json:"email" gorm:"type:varchar"` + Fullname string `json:"fullname" gorm:"type:varchar"` + Address *string `json:"address" gorm:"type:varchar"` + PhoneNumber *string `json:"phone_number" gorm:"type:varchar"` + WorkType *string `json:"work_type" gorm:"type:varchar"` + GenderType *string `json:"gender_type" gorm:"type:varchar"` + IdentityType *string `json:"identity_type" gorm:"type:varchar"` + IdentityGroup *string `json:"identity_group" gorm:"type:varchar"` + IdentityGroupNumber *string `json:"identity_group_number" gorm:"type:varchar"` + IdentityNumber *string `json:"identity_number" gorm:"type:varchar"` + DateOfBirth *string `json:"date_of_birth" gorm:"type:varchar"` + LastEducation *string `json:"last_education" gorm:"type:varchar"` + Degree *string `json:"degree" gorm:"type:varchar"` + WhatsappNumber *string `json:"whatsapp_number" gorm:"type:varchar"` + LastJobTitle *string `json:"last_job_title" gorm:"type:varchar"` + UserRoleId uint `json:"user_role_id" gorm:"type:int4"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4"` + UserLevel *userLevels.UserLevels `json:"user_levels" gorm:"foreignKey:UserLevelId;references:ID"` + KeycloakId *string `json:"keycloak_id" gorm:"type:varchar"` + StatusId *int `json:"status_id" gorm:"type:int4;default:1"` + CreatedById *uint `json:"created_by_id" gorm:"type:int4"` + ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"` + TempPassword *string `json:"temp_password" gorm:"type:varchar"` + IsEmailUpdated *bool `json:"is_email_updated" gorm:"type:bool;default:false"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} diff --git a/app/database/entity/work_history.entity.go b/app/database/entity/work_history.entity.go new file mode 100644 index 0000000..4cce7e8 --- /dev/null +++ b/app/database/entity/work_history.entity.go @@ -0,0 +1,18 @@ +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type WorkHistory struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null;index"` + JobTitle string `json:"job_title" gorm:"type:varchar;not null"` + CompanyName string `json:"company_name" gorm:"type:varchar;not null"` + StartDate time.Time `json:"start_date" gorm:"not null"` + EndDate *time.Time `json:"end_date"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} diff --git a/app/database/index.database.go b/app/database/index.database.go new file mode 100644 index 0000000..2b91014 --- /dev/null +++ b/app/database/index.database.go @@ -0,0 +1,151 @@ +package database + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/database/entity/article_category_details" + "narasi-ahli-be/app/database/entity/user_levels" + "narasi-ahli-be/app/database/entity/users" + "narasi-ahli-be/config/config" + + "github.com/rs/zerolog" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +// Database setup database with gorm +type Database struct { + DB *gorm.DB + Log zerolog.Logger + Cfg *config.Config +} + +type Seeder interface { + Seed(*gorm.DB) error + Count(*gorm.DB) (int, error) +} + +func NewDatabase(cfg *config.Config, log zerolog.Logger) *Database { + db := &Database{ + Cfg: cfg, + Log: log, + } + + return db +} + +// ConnectDatabase connect database +func (_db *Database) ConnectDatabase() { + logMode := _db.Cfg.DB.Postgres.LogMode + var logLevel logger.LogLevel + if logMode == "INFO" { + logLevel = logger.Info + } else if logMode == "WARN" { + logLevel = logger.Warn + } else if logMode == "ERROR" { + logLevel = logger.Error + } else if logMode == "NONE" { + logLevel = logger.Silent + } + conn, err := gorm.Open(postgres.Open(_db.Cfg.DB.Postgres.DSN), &gorm.Config{ + Logger: logger.Default.LogMode(logLevel), + }) + if err != nil { + _db.Log.Error().Err(err).Msg("An unknown error occurred when to connect the database!") + } else { + _db.Log.Info().Msg("Connected the database succesfully!") + } + + _db.DB = conn +} + +// ShutdownDatabase shutdown database +func (_db *Database) ShutdownDatabase() { + sqlDB, err := _db.DB.DB() + if err != nil { + _db.Log.Error().Err(err).Msg("An unknown error occurred when to shutdown the database!") + } else { + _db.Log.Info().Msg("Shutdown the database succesfully!") + } + sqlDB.Close() +} + +// MigrateModels migrate models +func (_db *Database) MigrateModels() { + err := _db.DB.AutoMigrate( + Models()..., + ) + if err != nil { + _db.Log.Error().Err(err).Msg("An unknown error occurred when to migrate the database!") + } else { + _db.Log.Info().Msg("Migrate the database entity succesfully!") + } +} + +// Models list of models for migration +func Models() []interface{} { + return []interface{}{ + entity.ActivityLogs{}, + entity.ActivityLogTypes{}, + entity.Advertisement{}, + entity.Articles{}, + entity.ArticleCategories{}, + entity.ArticleApprovals{}, + article_category_details.ArticleCategoryDetails{}, + entity.ArticleFiles{}, + entity.ArticleComments{}, + entity.AuditTrails{}, + entity.Cities{}, + entity.CsrfTokenRecords{}, + entity.CustomStaticPages{}, + entity.Districts{}, + entity.Feedbacks{}, + entity.ForgotPasswords{}, + entity.Magazines{}, + entity.MagazineFiles{}, + entity.MasterMenus{}, + entity.MasterModules{}, + entity.MasterStatuses{}, + entity.MasterApprovalStatuses{}, + entity.Provinces{}, + entity.OneTimePasswords{}, + entity.Subscription{}, + user_levels.UserLevels{}, + entity.UserRoles{}, + entity.UserRoleAccesses{}, + users.Users{}, + entity.UserRoleLevelDetails{}, + + // Narasi Ahli entities + entity.EducationHistory{}, + entity.WorkHistory{}, + entity.ResearchJournals{}, + entity.Conversations{}, + entity.ChatMessages{}, + entity.AIChatSessions{}, + entity.AIChatMessages{}, + entity.AIChatLogs{}, + } +} + +// SeedModels seed data +func (_db *Database) SeedModels(seeder []Seeder) { + for _, seed := range seeder { + count, err := seed.Count(_db.DB) + if err != nil { + _db.Log.Error().Err(err).Msg("An unknown error occurred when to seed the database!") + } + + if count == 0 { + if err := seed.Seed(_db.DB); err != nil { + _db.Log.Error().Err(err).Msg("An unknown error occurred when to seed the database!") + } + + _db.Log.Info().Msg("Seeded the database successfully!") + } else { + _db.Log.Info().Msg("Database is already seeded!") + } + } + + _db.Log.Info().Msg("Seeded the database succesfully!") +} diff --git a/app/database/seeds/activity_log_types.seeds.go b/app/database/seeds/activity_log_types.seeds.go new file mode 100644 index 0000000..990169b --- /dev/null +++ b/app/database/seeds/activity_log_types.seeds.go @@ -0,0 +1,51 @@ +package seeds + +import ( + "narasi-ahli-be/app/database/entity" + + "gorm.io/gorm" +) + +type ActivityLogsSeeder struct{} + +var activityLogTypes = []entity.ActivityLogTypes{ + { + ID: 1, + Name: "Login", + IsActive: true, + }, + { + ID: 2, + Name: "View", + IsActive: true, + }, + { + ID: 3, + Name: "Share", + IsActive: true, + }, + { + ID: 4, + Name: "Comment", + IsActive: true, + }, +} + +func (ActivityLogsSeeder) Seed(conn *gorm.DB) error { + for _, row := range activityLogTypes { + if err := conn.Create(&row).Error; err != nil { + return err + } + } + + return nil +} + +func (ActivityLogsSeeder) Count(conn *gorm.DB) (int, error) { + var count int64 + if err := conn.Model(&entity.ActivityLogTypes{}).Count(&count).Error; err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/app/database/seeds/master_approval_statuses.seeds.go b/app/database/seeds/master_approval_statuses.seeds.go new file mode 100644 index 0000000..3319fe9 --- /dev/null +++ b/app/database/seeds/master_approval_statuses.seeds.go @@ -0,0 +1,46 @@ +package seeds + +import ( + "narasi-ahli-be/app/database/entity" + + "gorm.io/gorm" +) + +type MasterApprovalStatusesSeeder struct{} + +var masterApprovalStatuses = []entity.MasterApprovalStatuses{ + { + ID: 1, + Name: "Accepted", + IsActive: true, + }, + { + ID: 2, + Name: "Need Update", + IsActive: true, + }, + { + ID: 3, + Name: "Rejected", + IsActive: true, + }, +} + +func (MasterApprovalStatusesSeeder) Seed(conn *gorm.DB) error { + for _, row := range masterApprovalStatuses { + if err := conn.Create(&row).Error; err != nil { + return err + } + } + + return nil +} + +func (MasterApprovalStatusesSeeder) Count(conn *gorm.DB) (int, error) { + var count int64 + if err := conn.Model(&entity.MasterApprovalStatuses{}).Count(&count).Error; err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/app/database/seeds/master_statuses.seeds.go b/app/database/seeds/master_statuses.seeds.go new file mode 100644 index 0000000..9af2c9e --- /dev/null +++ b/app/database/seeds/master_statuses.seeds.go @@ -0,0 +1,46 @@ +package seeds + +import ( + "narasi-ahli-be/app/database/entity" + + "gorm.io/gorm" +) + +type MasterStatusesSeeder struct{} + +var masterStatuses = []entity.MasterStatuses{ + { + ID: 1, + Name: "Waiting", + IsActive: true, + }, + { + ID: 2, + Name: "Active", + IsActive: true, + }, + { + ID: 3, + Name: "Inactive", + IsActive: true, + }, +} + +func (MasterStatusesSeeder) Seed(conn *gorm.DB) error { + for _, row := range masterStatuses { + if err := conn.Create(&row).Error; err != nil { + return err + } + } + + return nil +} + +func (MasterStatusesSeeder) Count(conn *gorm.DB) (int, error) { + var count int64 + if err := conn.Model(&entity.MasterStatuses{}).Count(&count).Error; err != nil { + return 0, err + } + + return int(count), nil +} diff --git a/app/go-humas-be.exe b/app/go-humas-be.exe new file mode 100644 index 0000000..1fc8f85 Binary files /dev/null and b/app/go-humas-be.exe differ diff --git a/app/middleware/audit_trails.middleware.go b/app/middleware/audit_trails.middleware.go new file mode 100644 index 0000000..4c62753 --- /dev/null +++ b/app/middleware/audit_trails.middleware.go @@ -0,0 +1,69 @@ +package middleware + +import ( + "encoding/json" + "log" + "narasi-ahli-be/app/database/entity" + utilSvc "narasi-ahli-be/utils/service" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" +) + +func AuditTrailsMiddleware(db *gorm.DB) fiber.Handler { + return func(c *fiber.Ctx) error { + start := time.Now() + requestBody := c.Body() + + headersMap := c.GetReqHeaders() + headersJSON, _ := json.Marshal(headersMap) + + authHeader := c.Get("Authorization") + userId := utilSvc.GetUserId(authHeader) + + err := c.Next() + + audit := entity.AuditTrails{ + Method: c.Method(), + Path: c.OriginalURL(), + IP: getIP(c), + Status: c.Response().StatusCode(), + UserID: userId, + RequestHeaders: string(headersJSON), + RequestBody: string(requestBody), + ResponseBody: string(c.Response().Body()), + DurationMs: time.Since(start).Milliseconds(), + CreatedAt: time.Now(), + } + + go db.Create(&audit) + + return err + } +} + +func StartAuditTrailCleanup(db *gorm.DB, retention int) { + go func() { + for { + time.Sleep(24 * time.Hour) + + cutoff := time.Now().AddDate(0, 0, retention) + db.Where("created_at < ?", cutoff).Delete(&entity.AuditTrails{}) + + log.Printf(" at: %s", cutoff) + } + }() +} + +func getIP(c *fiber.Ctx) string { + ip := c.Get("X-Forwarded-For") + if ip == "" { + ip = c.IP() + } + if strings.Contains(ip, ":") { + ip = strings.Split(ip, ":")[0] + } + return ip +} diff --git a/app/middleware/csrf.middleware.go b/app/middleware/csrf.middleware.go new file mode 100644 index 0000000..fb9598d --- /dev/null +++ b/app/middleware/csrf.middleware.go @@ -0,0 +1,79 @@ +package middleware + +import ( + "fmt" + "narasi-ahli-be/app/database/entity" + "time" + + "gorm.io/gorm" +) + +type PostgresStorage struct { + DB *gorm.DB +} + +func (s *PostgresStorage) Get(key string) ([]byte, error) { + //log.Printf("CSRF Storage: Get token %s", key) + + var record entity.CsrfTokenRecords + result := s.DB.Where("token = ?", key).First(&record) + + if result.Error != nil { + //log.Printf("CSRF Storage Get error: %v for token: %s", result.Error, key) + return nil, result.Error + } + + if record.ExpireAt.Before(time.Now()) { + //log.Printf("CSRF token %s is expired", key) + return nil, fmt.Errorf("CSRF token is expired") + } + + return record.Value, nil +} + +func (s *PostgresStorage) Set(key string, value []byte, exp time.Duration) error { + //log.Printf("CSRF Storage: Setting token %s with expiration %v", key, exp) + + // Calculate expiration time + expireAt := time.Now().Add(exp) + + // Try to update existing record first + result := s.DB.Model(&entity.CsrfTokenRecords{}). + Where("token = ?", key). + Updates(map[string]interface{}{ + "expire_at": expireAt, + }) + + // If no rows were affected (not found), create a new record + if result.RowsAffected == 0 { + record := entity.CsrfTokenRecords{ + Token: key, + Value: value, + ExpireAt: expireAt, + CreatedAt: time.Now(), + } + + if err := s.DB.Create(&record).Error; err != nil { + //log.Printf("CSRF Storage: Error saving token: %v", err) + return err + } + } else if result.Error != nil { + //log.Printf("CSRF Storage: Error updating token: %v", result.Error) + return result.Error + } + + //log.Printf("CSRF Storage: Successfully saved/updated token") + return nil +} + +func (s *PostgresStorage) Delete(key string) error { + return s.DB.Where("token = ?", key).Delete(&entity.CsrfTokenRecords{}).Error +} + +func (s *PostgresStorage) Reset() error { + return s.DB.Where("expire_at < ?", time.Now()).Delete(&entity.CsrfTokenRecords{}).Error +} + +func (s *PostgresStorage) Close() error { + return nil +} diff --git a/app/middleware/register.middleware.go b/app/middleware/register.middleware.go new file mode 100644 index 0000000..d323cec --- /dev/null +++ b/app/middleware/register.middleware.go @@ -0,0 +1,166 @@ +package middleware + +import ( + "log" + "narasi-ahli-be/app/database" + "narasi-ahli-be/config/config" + utilsSvc "narasi-ahli-be/utils" + "time" + + "github.com/gofiber/fiber/v2/middleware/csrf" + "github.com/gofiber/fiber/v2/middleware/session" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/compress" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/limiter" + "github.com/gofiber/fiber/v2/middleware/monitor" + "github.com/gofiber/fiber/v2/middleware/pprof" + "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v2/utils" +) + +// Middleware is a struct that contains all the middleware functions +type Middleware struct { + App *fiber.App + Cfg *config.Config +} + +func NewMiddleware(app *fiber.App, cfg *config.Config) *Middleware { + return &Middleware{ + App: app, + Cfg: cfg, + } +} + +// Register registers all the middleware functions +func (m *Middleware) Register(db *database.Database) { + // Add Extra Middlewares + + m.App.Use(limiter.New(limiter.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Limiter.Enable), + Max: m.Cfg.Middleware.Limiter.Max, + Expiration: m.Cfg.Middleware.Limiter.Expiration * time.Second, + })) + + m.App.Use(compress.New(compress.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Compress.Enable), + Level: m.Cfg.Middleware.Compress.Level, + })) + + m.App.Use(recover.New(recover.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Recover.Enable), + })) + + m.App.Use(pprof.New(pprof.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Pprof.Enable), + })) + + m.App.Use(cors.New(cors.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Cors.Enable), + AllowOrigins: "http://localhost:3000, http://localhost:4000, https://dev.mikulnews.com, https://n8n.qudoco.com, https://narasiahli.com", + AllowMethods: "HEAD, GET, POST, PUT, DELETE, OPTION, PATCH", + AllowHeaders: "Origin, Content-Type, Accept, Accept-Language, Authorization, X-Requested-With, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Origin, Access-Control-Allow-Credentials, X-Csrf-Token, Cookie, Set-Cookie, X-Client-Key", + ExposeHeaders: "Content-Length, Content-Type", + AllowCredentials: true, + MaxAge: 12, + })) + + //=============================== + // CSRF CONFIG + //=============================== + + // Custom storage for CSRF + csrfSessionStorage := &PostgresStorage{ + DB: db.DB, + } + + // Store initialization for session + store := session.New(session.Config{ + CookieSameSite: m.Cfg.Middleware.Csrf.CookieSameSite, + CookieSecure: m.Cfg.Middleware.Csrf.CookieSecure, + CookieSessionOnly: m.Cfg.Middleware.Csrf.CookieSessionOnly, + CookieHTTPOnly: m.Cfg.Middleware.Csrf.CookieHttpOnly, + Storage: csrfSessionStorage, + }) + + m.App.Use(func(c *fiber.Ctx) error { + sess, err := store.Get(c) + if err != nil { + return err + } + c.Locals("session", sess) + return c.Next() + }) + + // Cleanup the expired token + go func() { + ticker := time.NewTicker(1 * time.Hour) + defer ticker.Stop() + + for range ticker.C { + if err := csrfSessionStorage.Reset(); err != nil { + log.Printf("Error cleaning up expired CSRF tokens: %v", err) + } + } + }() + + m.App.Use(csrf.New(csrf.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Csrf.Enable), + KeyLookup: "header:" + csrf.HeaderName, + CookieName: m.Cfg.Middleware.Csrf.CookieName, + CookieSameSite: m.Cfg.Middleware.Csrf.CookieSameSite, + CookieSecure: m.Cfg.Middleware.Csrf.CookieSecure, + CookieSessionOnly: m.Cfg.Middleware.Csrf.CookieSessionOnly, + CookieHTTPOnly: m.Cfg.Middleware.Csrf.CookieHttpOnly, + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUIDv4, + ContextKey: "csrf", + ErrorHandler: func(c *fiber.Ctx, err error) error { + return utilsSvc.CsrfErrorHandler(c, err) + }, + Extractor: csrf.CsrfFromHeader(csrf.HeaderName), + Session: store, + SessionKey: "fiber.csrf.token", + })) + + //=============================== + m.App.Use(AuditTrailsMiddleware(db.DB)) + StartAuditTrailCleanup(db.DB, m.Cfg.Middleware.AuditTrails.Retention) + + //m.App.Use(filesystem.New(filesystem.Config{ + // Next: utils.IsEnabled(m.Cfg.Middleware.FileSystem.Enable), + // Root: http.Dir(m.Cfg.Middleware.FileSystem.Root), + // Browse: m.Cfg.Middleware.FileSystem.Browse, + // MaxAge: m.Cfg.Middleware.FileSystem.MaxAge, + //})) + + // ================================================== + + m.App.Get(m.Cfg.Middleware.Monitor.Path, monitor.New(monitor.Config{ + Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Monitor.Enable), + })) + + // Route for generate CSRF token + m.App.Get("/csrf-token", func(c *fiber.Ctx) error { + // Retrieve CSRF token from Fiber's middleware context + token, ok := c.Locals("csrf").(string) + + //c.Context().VisitUserValues(func(key []byte, value interface{}) { + // log.Printf("Local Key: %s, Value: %v", key, value) + //}) + + if !ok || token == "" { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "code": 500, + "messages": []string{"Failed to retrieve CSRF token"}, + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "csrf_token": token, + }) + }) +} diff --git a/app/module/activity_logs/activity_logs.module.go b/app/module/activity_logs/activity_logs.module.go new file mode 100644 index 0000000..3a03f89 --- /dev/null +++ b/app/module/activity_logs/activity_logs.module.go @@ -0,0 +1,55 @@ +package activity_logs + +import ( + "narasi-ahli-be/app/module/activity_logs/controller" + "narasi-ahli-be/app/module/activity_logs/repository" + "narasi-ahli-be/app/module/activity_logs/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ActivityLogsRouter +type ActivityLogsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ActivityLogs module +var NewActivityLogsModule = fx.Options( + // register repository of ActivityLogs module + fx.Provide(repository.NewActivityLogsRepository), + + // register service of ActivityLogs module + fx.Provide(service.NewActivityLogsService), + + // register controller of ActivityLogs module + fx.Provide(controller.NewController), + + // register router of ActivityLogs module + fx.Provide(NewActivityLogsRouter), +) + +// init ActivityLogsRouter +func NewActivityLogsRouter(fiber *fiber.App, controller *controller.Controller) *ActivityLogsRouter { + return &ActivityLogsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ActivityLogs module +func (_i *ActivityLogsRouter) RegisterActivityLogsRoutes() { + // define controllers + activityLogsController := _i.Controller.ActivityLogs + + // define routes + _i.App.Route("/activity-logs", func(router fiber.Router) { + router.Get("/", activityLogsController.All) + router.Get("/statistics", activityLogsController.GetActivityStats) + router.Get("/detail/:id", activityLogsController.Show) + router.Post("/", activityLogsController.Save) + router.Put("/:id", activityLogsController.Update) + router.Delete("/:id", activityLogsController.Delete) + }) +} diff --git a/app/module/activity_logs/controller/activity_logs.controller.go b/app/module/activity_logs/controller/activity_logs.controller.go new file mode 100644 index 0000000..74acbfa --- /dev/null +++ b/app/module/activity_logs/controller/activity_logs.controller.go @@ -0,0 +1,250 @@ +package controller + +import ( + "narasi-ahli-be/app/module/activity_logs/request" + "narasi-ahli-be/app/module/activity_logs/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + "strings" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" +) + +type activityLogsController struct { + activityLogsService service.ActivityLogsService + Log zerolog.Logger +} + +type ActivityLogsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + GetActivityStats(c *fiber.Ctx) error +} + +func NewActivityLogsController(activityLogsService service.ActivityLogsService, log zerolog.Logger) ActivityLogsController { + return &activityLogsController{ + activityLogsService: activityLogsService, + Log: log, + } +} + +// All get all ActivityLogs +// @Summary Get all ActivityLogs +// @Description API for getting all ActivityLogs +// @Tags ActivityLogs +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.ActivityLogsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /activity-logs [get] +func (_i *activityLogsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.ActivityLogsQueryRequestContext{ + ActivityTypeId: c.Query("activityTypeId"), + Url: c.Query("url"), + ArticleId: c.Query("articleId"), + UserId: c.Query("userId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + activityLogsData, paging, err := _i.activityLogsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ActivityLogs list successfully retrieved"}, + Data: activityLogsData, + Meta: paging, + }) +} + +// Show get one ActivityLogs +// @Summary Get one ActivityLogs +// @Description API for getting one ActivityLogs +// @Tags ActivityLogs +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "ActivityLogs ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /activity-logs/detail/{id} [get] +func (_i *activityLogsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + activityLogsData, err := _i.activityLogsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ActivityLogs successfully retrieved"}, + Data: activityLogsData, + }) +} + +// Save create ActivityLogs +// @Summary Create ActivityLogs +// @Description API for create ActivityLogs +// @Tags ActivityLogs +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ActivityLogsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /activity-logs [post] +func (_i *activityLogsController) Save(c *fiber.Ctx) error { + req := new(request.ActivityLogsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + var authToken *string + getTokenFromHeader := c.Get("Authorization") + if getTokenFromHeader == "" { + authToken = nil + } else { + authToken = &getTokenFromHeader + } + visitorIp := GetVisitorIP(c) + req.VisitorIp = &visitorIp + dataResult, err := _i.activityLogsService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ActivityLogs successfully created"}, + Data: dataResult, + }) +} + +// Update update ActivityLogs +// @Summary update ActivityLogs +// @Description API for update ActivityLogs +// @Tags ActivityLogs +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ActivityLogsUpdateRequest true "Required payload" +// @Param id path int true "ActivityLogs ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /activity-logs/{id} [put] +func (_i *activityLogsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ActivityLogsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.activityLogsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ActivityLogs successfully updated"}, + }) +} + +// Delete delete ActivityLogs +// @Summary delete ActivityLogs +// @Description API for delete ActivityLogs +// @Tags ActivityLogs +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "ActivityLogs ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /activity-logs/{id} [delete] +func (_i *activityLogsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.activityLogsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ActivityLogs successfully deleted"}, + }) +} + +// GetActivityStats get activity stats ActivityLogs +// @Summary Get activity stats ActivityLogs +// @Description API for get activity stats ActivityLogs +// @Tags ActivityLogs +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /activity-logs/statistics [get] +func (_i *activityLogsController) GetActivityStats(c *fiber.Ctx) error { + _i.Log.Info().Interface("GetActivityStats", "checker controller").Msg("") + + activityStatsData, err := _i.activityLogsService.GetActivityStats() + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ActivityLogs Stats successfully retrieved"}, + Data: activityStatsData, + }) +} + +func GetVisitorIP(c *fiber.Ctx) string { + ip := c.Get("X-Forwarded-For") + if ip == "" { + ip = c.IP() + } + if strings.Contains(ip, ":") { + ip = strings.Split(ip, ":")[0] + } + return ip +} diff --git a/app/module/activity_logs/controller/controller.go b/app/module/activity_logs/controller/controller.go new file mode 100644 index 0000000..58df32b --- /dev/null +++ b/app/module/activity_logs/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/activity_logs/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + ActivityLogs ActivityLogsController +} + +func NewController(ActivityLogsService service.ActivityLogsService, log zerolog.Logger) *Controller { + return &Controller{ + ActivityLogs: NewActivityLogsController(ActivityLogsService, log), + } +} diff --git a/app/module/activity_logs/mapper/activity_logs.mapper.go b/app/module/activity_logs/mapper/activity_logs.mapper.go new file mode 100644 index 0000000..71f9877 --- /dev/null +++ b/app/module/activity_logs/mapper/activity_logs.mapper.go @@ -0,0 +1,20 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/activity_logs/response" +) + +func ActivityLogsResponseMapper(activityLogsReq *entity.ActivityLogs) (activityLogsRes *res.ActivityLogsResponse) { + if activityLogsReq != nil { + activityLogsRes = &res.ActivityLogsResponse{ + ID: activityLogsReq.ID, + ActivityTypeId: activityLogsReq.ActivityTypeId, + Url: activityLogsReq.Url, + ArticleId: activityLogsReq.ArticleId, + UserId: activityLogsReq.UserId, + CreatedAt: activityLogsReq.CreatedAt, + } + } + return activityLogsRes +} diff --git a/app/module/activity_logs/repository/activity_logs.repository.go b/app/module/activity_logs/repository/activity_logs.repository.go new file mode 100644 index 0000000..0a9f57e --- /dev/null +++ b/app/module/activity_logs/repository/activity_logs.repository.go @@ -0,0 +1,124 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/activity_logs/request" + "narasi-ahli-be/utils/paginator" + "strings" + "time" + + "github.com/rs/zerolog" +) + +type activityLogsRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// ActivityLogsRepository define interface of IActivityLogsRepository +type ActivityLogsRepository interface { + GetAll(req request.ActivityLogsQueryRequest) (activityLogss []*entity.ActivityLogs, paging paginator.Pagination, err error) + FindOne(id uint) (activityLogs *entity.ActivityLogs, err error) + Create(activityLogs *entity.ActivityLogs) (activityLogsReturn *entity.ActivityLogs, err error) + Update(id uint, activityLogs *entity.ActivityLogs) (err error) + Delete(id uint) (err error) + CountUniqueVisitorAllTime() (count int64, err error) + CountUniqueVisitorToday() (count int64, err error) + CountTotalViewAllTime() (count int64, err error) +} + +func NewActivityLogsRepository(db *database.Database, logger zerolog.Logger) ActivityLogsRepository { + return &activityLogsRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of IActivityLogsRepository +func (_i *activityLogsRepository) GetAll(req request.ActivityLogsQueryRequest) (activityLogss []*entity.ActivityLogs, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.ActivityLogs{}) + + if req.ActivityTypeId != nil { + query = query.Where("activity_type_id = ?", req.ActivityTypeId) + } + if req.Url != nil && *req.Url != "" { + url := strings.ToLower(*req.Url) + query = query.Where("LOWER(url) LIKE ?", "%"+strings.ToLower(url)+"%") + } + if req.ArticleId != nil { + query = query.Where("article_id = ?", req.ArticleId) + } + if req.UserId != nil { + query = query.Where("user_id = ?", req.UserId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&activityLogss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *activityLogsRepository) FindOne(id uint) (activityLogs *entity.ActivityLogs, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.First(&activityLogs).Error; err != nil { + return nil, err + } + + return activityLogs, nil +} + +func (_i *activityLogsRepository) Create(activityLogs *entity.ActivityLogs) (activityLogsReturn *entity.ActivityLogs, err error) { + result := _i.DB.DB.Create(activityLogs) + return activityLogs, result.Error +} + +func (_i *activityLogsRepository) Update(id uint, activityLogs *entity.ActivityLogs) (err error) { + query := _i.DB.DB.Model(&entity.ActivityLogs{}).Where(&entity.ActivityLogs{ID: id}) + return query.Updates(activityLogs).Error +} + +func (_i *activityLogsRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&entity.ActivityLogs{}).Where("id = ?", id) + return query.Delete(&entity.ActivityLogs{}).Error +} + +func (_i *activityLogsRepository) CountUniqueVisitorAllTime() (count int64, err error) { + query := _i.DB.DB.Model(&entity.ActivityLogs{}) + err = query.Distinct("visitor_ip").Count(&count).Error + return +} + +func (_i *activityLogsRepository) CountTotalViewAllTime() (count int64, err error) { + query := _i.DB.DB.Model(&entity.ActivityLogs{}).Where("activity_type_id = ?", 2) + err = query.Count(&count).Error + return +} + +func (_i *activityLogsRepository) CountUniqueVisitorToday() (count int64, err error) { + tenMinutesAgo := time.Now().Add(-10 * time.Minute) + + query := _i.DB.DB.Model(&entity.AuditTrails{}).Where("created_at >= ?", tenMinutesAgo) + err = query.Select("ip").Group("ip").Count(&count).Error + return +} diff --git a/app/module/activity_logs/request/activity_logs.request.go b/app/module/activity_logs/request/activity_logs.request.go new file mode 100644 index 0000000..4f30902 --- /dev/null +++ b/app/module/activity_logs/request/activity_logs.request.go @@ -0,0 +1,92 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type ActivityLogsGeneric interface { + ToEntity() +} + +type ActivityLogsQueryRequest struct { + ActivityTypeId *int `json:"activityTypeId"` + Url *string `json:"url"` + ArticleId *int `json:"articleId"` + UserId *int `json:"userId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ActivityLogsCreateRequest struct { + ActivityTypeId int `json:"activityTypeId" validate:"required"` + Url string `json:"url" validate:"required"` + ArticleId *uint `json:"articleId"` + UserId *uint `json:"userId"` + VisitorIp *string `json:"visitorIp"` +} + +func (req ActivityLogsCreateRequest) ToEntity() *entity.ActivityLogs { + return &entity.ActivityLogs{ + ActivityTypeId: req.ActivityTypeId, + Url: req.Url, + ArticleId: req.ArticleId, + UserId: req.UserId, + VisitorIp: req.VisitorIp, + CreatedAt: time.Now(), + } +} + +type ActivityLogsUpdateRequest struct { + ID uint `json:"id" validate:"required"` + ActivityTypeId int `json:"activityTypeId" validate:"required"` + Url string `json:"url" validate:"required"` + ArticleId *uint `json:"articleId"` + UserId *uint `json:"userId"` +} + +func (req ActivityLogsUpdateRequest) ToEntity() *entity.ActivityLogs { + return &entity.ActivityLogs{ + ID: req.ID, + ActivityTypeId: req.ActivityTypeId, + Url: req.Url, + ArticleId: req.ArticleId, + UserId: req.UserId, + } +} + +type ActivityLogsQueryRequestContext struct { + ActivityTypeId string `json:"activityTypeId"` + Url string `json:"url"` + ArticleId string `json:"articleId"` + UserId string `json:"userId"` +} + +func (req ActivityLogsQueryRequestContext) ToParamRequest() ActivityLogsQueryRequest { + var request ActivityLogsQueryRequest + + if activityTypeIdStr := req.ActivityTypeId; activityTypeIdStr != "" { + activityTypeId, err := strconv.Atoi(activityTypeIdStr) + if err == nil { + request.ActivityTypeId = &activityTypeId + } + } + if url := req.Url; url != "" { + request.Url = &url + } + if articleIdStr := req.ArticleId; articleIdStr != "" { + articleId, err := strconv.Atoi(articleIdStr) + if err == nil { + request.ArticleId = &articleId + } + } + if userIdStr := req.UserId; userIdStr != "" { + userId, err := strconv.Atoi(userIdStr) + if err == nil { + request.UserId = &userId + } + } + + return request +} diff --git a/app/module/activity_logs/response/activity_logs.response.go b/app/module/activity_logs/response/activity_logs.response.go new file mode 100644 index 0000000..6c2040e --- /dev/null +++ b/app/module/activity_logs/response/activity_logs.response.go @@ -0,0 +1,18 @@ +package response + +import "time" + +type ActivityLogsResponse struct { + ID uint `json:"id"` + ActivityTypeId int `json:"activityTypeId"` + Url string `json:"url"` + ArticleId *uint `json:"articleId"` + UserId *uint `json:"userId"` + CreatedAt time.Time `json:"createdAt"` +} + +type ActivityStatsResponse struct { + TotalVisitorAllTime int64 `json:"totalVisitorAllTime"` + TotalVisitorToday int64 `json:"totalVisitorToday"` + TotalViewAllTime int64 `json:"totalViewAllTime"` +} diff --git a/app/module/activity_logs/service/activity_logs.service.go b/app/module/activity_logs/service/activity_logs.service.go new file mode 100644 index 0000000..2d2293b --- /dev/null +++ b/app/module/activity_logs/service/activity_logs.service.go @@ -0,0 +1,124 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/activity_logs/mapper" + "narasi-ahli-be/app/module/activity_logs/repository" + "narasi-ahli-be/app/module/activity_logs/request" + "narasi-ahli-be/app/module/activity_logs/response" + "narasi-ahli-be/app/module/articles/service" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + + "github.com/rs/zerolog" +) + +// ActivityLogsService +type activityLogsService struct { + Repo repository.ActivityLogsRepository + UsersRepo usersRepository.UsersRepository + ArticleService service.ArticlesService + Log zerolog.Logger +} + +// ActivityLogsService define interface of IActivityLogsService +type ActivityLogsService interface { + All(req request.ActivityLogsQueryRequest) (activityLogs []*response.ActivityLogsResponse, paging paginator.Pagination, err error) + Show(id uint) (activityLogs *response.ActivityLogsResponse, err error) + Save(req request.ActivityLogsCreateRequest, authToken *string) (activityLogs *entity.ActivityLogs, err error) + Update(id uint, req request.ActivityLogsUpdateRequest) (err error) + Delete(id uint) error + GetActivityStats() (activityStats *response.ActivityStatsResponse, err error) +} + +// NewActivityLogsService init ActivityLogsService +func NewActivityLogsService(repo repository.ActivityLogsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository, articleService service.ArticlesService) ActivityLogsService { + + return &activityLogsService{ + Repo: repo, + Log: log, + UsersRepo: usersRepo, + ArticleService: articleService, + } +} + +// All implement interface of ActivityLogsService +func (_i *activityLogsService) All(req request.ActivityLogsQueryRequest) (activityLogss []*response.ActivityLogsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + activityLogss = append(activityLogss, mapper.ActivityLogsResponseMapper(result)) + } + + return +} + +func (_i *activityLogsService) Show(id uint) (activityLogs *response.ActivityLogsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.ActivityLogsResponseMapper(result), nil +} + +func (_i *activityLogsService) Save(req request.ActivityLogsCreateRequest, authToken *string) (activityLogs *entity.ActivityLogs, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + newReq := req.ToEntity() + + if authToken != nil { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, *authToken) + newReq.UserId = &createdBy.ID + } + + result, err := _i.Repo.Create(newReq) + if err != nil { + return nil, err + } + + // update article + err = _i.ArticleService.UpdateActivityCount(*req.ArticleId, req.ActivityTypeId) + if err != nil { + return nil, err + } + + return result, nil +} + +func (_i *activityLogsService) Update(id uint, req request.ActivityLogsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + return _i.Repo.Update(id, newReq) +} + +func (_i *activityLogsService) Delete(id uint) error { + return _i.Repo.Delete(id) +} + +func (_i *activityLogsService) GetActivityStats() (activityStats *response.ActivityStatsResponse, err error) { + _i.Log.Info().Interface("GetActivityStats", "checker").Msg("") + countUniqueVisitorAllTime, err := _i.Repo.CountUniqueVisitorAllTime() + if err != nil { + return nil, err + } + countUniqueVisitorToday, err := _i.Repo.CountUniqueVisitorToday() + if err != nil { + return nil, err + } + countTotalViewAllTime, err := _i.Repo.CountTotalViewAllTime() + if err != nil { + return nil, err + } + + getActivityStats := &response.ActivityStatsResponse{ + TotalVisitorAllTime: countUniqueVisitorAllTime, + TotalVisitorToday: countUniqueVisitorToday, + TotalViewAllTime: countTotalViewAllTime, + } + return getActivityStats, nil +} diff --git a/app/module/advertisement/advertisement.module.go b/app/module/advertisement/advertisement.module.go new file mode 100644 index 0000000..b3c3347 --- /dev/null +++ b/app/module/advertisement/advertisement.module.go @@ -0,0 +1,57 @@ +package advertisement + +import ( + "narasi-ahli-be/app/module/advertisement/controller" + "narasi-ahli-be/app/module/advertisement/repository" + "narasi-ahli-be/app/module/advertisement/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of AdvertisementRouter +type AdvertisementRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Advertisement module +var NewAdvertisementModule = fx.Options( + // register repository of Advertisement module + fx.Provide(repository.NewAdvertisementRepository), + + // register service of Advertisement module + fx.Provide(service.NewAdvertisementService), + + // register controller of Advertisement module + fx.Provide(controller.NewController), + + // register router of Advertisement module + fx.Provide(NewAdvertisementRouter), +) + +// init AdvertisementRouter +func NewAdvertisementRouter(fiber *fiber.App, controller *controller.Controller) *AdvertisementRouter { + return &AdvertisementRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Advertisement module +func (_i *AdvertisementRouter) RegisterAdvertisementRoutes() { + // define controllers + advertisementController := _i.Controller.Advertisement + + // define routes + _i.App.Route("/advertisement", func(router fiber.Router) { + router.Get("/", advertisementController.All) + router.Get("/:id", advertisementController.Show) + router.Post("/", advertisementController.Save) + router.Post("/upload/:id", advertisementController.Upload) + router.Get("/viewer/:filename", advertisementController.Viewer) + router.Put("/:id", advertisementController.Update) + router.Put("/publish/:id", advertisementController.UpdatePublish) + router.Delete("/:id", advertisementController.Delete) + }) +} diff --git a/app/module/advertisement/controller/advertisement.controller.go b/app/module/advertisement/controller/advertisement.controller.go new file mode 100644 index 0000000..eaa8ec5 --- /dev/null +++ b/app/module/advertisement/controller/advertisement.controller.go @@ -0,0 +1,292 @@ +package controller + +import ( + "narasi-ahli-be/app/module/advertisement/request" + "narasi-ahli-be/app/module/advertisement/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type advertisementController struct { + advertisementService service.AdvertisementService + Log zerolog.Logger +} + +type AdvertisementController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Upload(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + UpdatePublish(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error +} + +func NewAdvertisementController(advertisementService service.AdvertisementService, log zerolog.Logger) AdvertisementController { + return &advertisementController{ + advertisementService: advertisementService, + Log: log, + } +} + +// All get all Advertisement +// @Summary Get all Advertisement +// @Description API for getting all Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.AdvertisementQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement [get] +func (_i *advertisementController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.AdvertisementQueryRequestContext{ + Title: c.Query("title"), + Description: c.Query("description"), + RedirectLink: c.Query("redirectLink"), + Placement: c.Query("placement"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + advertisementData, paging, err := _i.advertisementService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement list successfully retrieved"}, + Data: advertisementData, + Meta: paging, + }) +} + +// Show get one Advertisement +// @Summary Get one Advertisement +// @Description API for getting one Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Advertisement ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement/{id} [get] +func (_i *advertisementController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + advertisementData, err := _i.advertisementService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement successfully retrieved"}, + Data: advertisementData, + }) +} + +// Save create Advertisement +// @Summary Create Advertisement +// @Description API for create Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.AdvertisementCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement [post] +func (_i *advertisementController) Save(c *fiber.Ctx) error { + req := new(request.AdvertisementCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + dataResult, err := _i.advertisementService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement successfully created"}, + Data: dataResult, + }) +} + +// Upload Advertisement +// @Summary Upload Advertisement +// @Description API for Upload File Advertisement +// @Tags Advertisement +// @Security Bearer +// @Produce json +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param file formData file true "Upload file" multiple false +// @Param id path int true "Advertisement ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement/upload/{id} [post] +func (_i *advertisementController) Upload(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.advertisementService.Upload(c, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement successfully upload"}, + }) +} + +// Update update Advertisement +// @Summary update Advertisement +// @Description API for update Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.AdvertisementUpdateRequest true "Required payload" +// @Param id path int true "Advertisement ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement/{id} [put] +func (_i *advertisementController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.AdvertisementUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.advertisementService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement successfully updated"}, + }) +} + +// UpdatePublish Advertisement +// @Summary Update Publish Advertisement +// @Description API for Update Publish Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id path int true "Advertisement ID" +// @Param isPublish query bool true "Advertisement Publish Status" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement/publish/{id} [put] +func (_i *advertisementController) UpdatePublish(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + isPublish, err := strconv.ParseBool(c.Query("isPublish")) + if err != nil { + return err + } + + err = _i.advertisementService.UpdatePublish(uint(id), isPublish) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement successfully publish updated"}, + }) +} + +// Delete delete Advertisement +// @Summary delete Advertisement +// @Description API for delete Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Advertisement ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement/{id} [delete] +func (_i *advertisementController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.advertisementService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Advertisement successfully deleted"}, + }) +} + +// Viewer Advertisement +// @Summary Viewer Advertisement +// @Description API for Viewer Advertisement +// @Tags Advertisement +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param filename path string true "Content File Name" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /advertisement/viewer/{filename} [get] +func (_i *advertisementController) Viewer(c *fiber.Ctx) error { + return _i.advertisementService.Viewer(c) +} diff --git a/app/module/advertisement/controller/controller.go b/app/module/advertisement/controller/controller.go new file mode 100644 index 0000000..3494f69 --- /dev/null +++ b/app/module/advertisement/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/advertisement/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + Advertisement AdvertisementController +} + +func NewController(AdvertisementService service.AdvertisementService, log zerolog.Logger) *Controller { + return &Controller{ + Advertisement: NewAdvertisementController(AdvertisementService, log), + } +} diff --git a/app/module/advertisement/mapper/advertisement.mapper.go b/app/module/advertisement/mapper/advertisement.mapper.go new file mode 100644 index 0000000..c578aef --- /dev/null +++ b/app/module/advertisement/mapper/advertisement.mapper.go @@ -0,0 +1,28 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/advertisement/response" +) + +func AdvertisementResponseMapper(advertisementReq *entity.Advertisement, host string) (advertisementRes *res.AdvertisementResponse) { + if advertisementReq != nil { + advertisementRes = &res.AdvertisementResponse{ + ID: advertisementReq.ID, + Title: advertisementReq.Title, + Description: advertisementReq.Description, + RedirectLink: advertisementReq.RedirectLink, + Placement: advertisementReq.Placement, + StatusId: advertisementReq.StatusId, + IsActive: advertisementReq.IsActive, + IsPublish: advertisementReq.IsPublish, + CreatedAt: advertisementReq.CreatedAt, + UpdatedAt: advertisementReq.UpdatedAt, + } + + if advertisementReq.ContentFilePath != nil { + advertisementRes.ContentFileUrl = host + "/advertisement/viewer/" + *advertisementReq.ContentFileName + } + } + return advertisementRes +} diff --git a/app/module/advertisement/repository/advertisement.repository.go b/app/module/advertisement/repository/advertisement.repository.go new file mode 100644 index 0000000..a1a2415 --- /dev/null +++ b/app/module/advertisement/repository/advertisement.repository.go @@ -0,0 +1,124 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/advertisement/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + + "github.com/rs/zerolog" +) + +type advertisementRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// AdvertisementRepository define interface of IAdvertisementRepository +type AdvertisementRepository interface { + GetAll(req request.AdvertisementQueryRequest) (advertisements []*entity.Advertisement, paging paginator.Pagination, err error) + FindOne(id uint) (advertisement *entity.Advertisement, err error) + FindByFilename(contentFilename string) (advertisement *entity.Advertisement, err error) + Create(advertisement *entity.Advertisement) (advertisementReturn *entity.Advertisement, err error) + Update(id uint, advertisement *entity.Advertisement) (err error) + Delete(id uint) (err error) +} + +func NewAdvertisementRepository(db *database.Database, logger zerolog.Logger) AdvertisementRepository { + return &advertisementRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of IAdvertisementRepository +func (_i *advertisementRepository) GetAll(req request.AdvertisementQueryRequest) (advertisements []*entity.Advertisement, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Advertisement{}) + + query = query.Where("is_active = ?", true) + + if req.Title != nil && *req.Title != "" { + title := strings.ToLower(*req.Title) + query = query.Where("LOWER(title) LIKE ?", "%"+strings.ToLower(title)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.RedirectLink != nil && *req.RedirectLink != "" { + redirectLink := strings.ToLower(*req.RedirectLink) + query = query.Where("LOWER(redirect_link) LIKE ?", "%"+strings.ToLower(redirectLink)+"%") + } + if req.Placement != nil && *req.Placement != "" { + placement := strings.ToLower(*req.Placement) + query = query.Where("LOWER(placement) LIKE ?", "%"+strings.ToLower(placement)+"%") + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&advertisements).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *advertisementRepository) FindOne(id uint) (advertisement *entity.Advertisement, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.First(&advertisement).Error; err != nil { + return nil, err + } + + return advertisement, nil +} + +func (_i *advertisementRepository) FindByFilename(contentFilename string) (advertisement *entity.Advertisement, err error) { + query := _i.DB.DB.Where("content_file_name = ?", contentFilename) + + if err := query.First(&advertisement).Error; err != nil { + return nil, err + } + + return advertisement, nil +} + +func (_i *advertisementRepository) Create(advertisement *entity.Advertisement) (advertisementReturn *entity.Advertisement, err error) { + result := _i.DB.DB.Create(advertisement) + return advertisement, result.Error +} + +func (_i *advertisementRepository) Update(id uint, advertisement *entity.Advertisement) (err error) { + advertisementMap, err := utilSvc.StructToMap(advertisement) + if err != nil { + return err + } + query := _i.DB.DB.Model(&entity.Advertisement{}).Where(&entity.Advertisement{ID: id}) + return query.Updates(advertisementMap).Error +} + +func (_i *advertisementRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&entity.Advertisement{}).Where("id = ?", id) + return query.Delete(&entity.Advertisement{}).Error +} diff --git a/app/module/advertisement/request/advertisement.request.go b/app/module/advertisement/request/advertisement.request.go new file mode 100644 index 0000000..86bcbd8 --- /dev/null +++ b/app/module/advertisement/request/advertisement.request.go @@ -0,0 +1,100 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type AdvertisementGeneric interface { + ToEntity() +} + +type AdvertisementQueryRequest struct { + Title *string `json:"title"` + Description *string `json:"description"` + RedirectLink *string `json:"redirectLink"` + Placement *string `json:"placement"` + IsPublish *bool `json:"isPublish"` + StatusId *int `json:"statusId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type AdvertisementCreateRequest struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + RedirectLink string `json:"redirectLink" validate:"required"` + Placement string `json:"placement" validate:"required"` +} + +func (req AdvertisementCreateRequest) ToEntity() *entity.Advertisement { + return &entity.Advertisement{ + Title: req.Title, + Description: req.Description, + RedirectLink: req.RedirectLink, + Placement: req.Placement, + StatusId: 1, + IsPublish: true, + IsActive: true, + } +} + +type AdvertisementUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + RedirectLink string `json:"redirectLink" validate:"required"` + Placement string `json:"placement" validate:"required"` +} + +func (req AdvertisementUpdateRequest) ToEntity() *entity.Advertisement { + return &entity.Advertisement{ + ID: req.ID, + Title: req.Title, + Description: req.Description, + RedirectLink: req.RedirectLink, + Placement: req.Placement, + UpdatedAt: time.Now(), + } +} + +type AdvertisementQueryRequestContext struct { + Title string `json:"title"` + Description string `json:"description"` + RedirectLink string `json:"redirectLink"` + Placement string `json:"placement"` + StatusId string `json:"statusId"` + IsPublish string `json:"isPublish"` +} + +func (req AdvertisementQueryRequestContext) ToParamRequest() AdvertisementQueryRequest { + var request AdvertisementQueryRequest + + if title := req.Title; title != "" { + request.Title = &title + } + if description := req.Description; description != "" { + request.Description = &description + } + if redirectLink := req.RedirectLink; redirectLink != "" { + request.RedirectLink = &redirectLink + } + if placement := req.Placement; placement != "" { + request.Placement = &placement + } + if isPublishStr := req.IsPublish; isPublishStr != "" { + isPublish, err := strconv.ParseBool(isPublishStr) + if err == nil { + request.IsPublish = &isPublish + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/advertisement/response/advertisement.response.go b/app/module/advertisement/response/advertisement.response.go new file mode 100644 index 0000000..55db4aa --- /dev/null +++ b/app/module/advertisement/response/advertisement.response.go @@ -0,0 +1,17 @@ +package response + +import "time" + +type AdvertisementResponse struct { + ID uint `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + RedirectLink string `json:"redirectLink"` + ContentFileUrl string `json:"contentFileUrl"` + Placement string `json:"placement"` + StatusId int `json:"statusId"` + IsPublish bool `json:"isPublish"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/advertisement/service/advertisement.service.go b/app/module/advertisement/service/advertisement.service.go new file mode 100644 index 0000000..52b6544 --- /dev/null +++ b/app/module/advertisement/service/advertisement.service.go @@ -0,0 +1,264 @@ +package service + +import ( + "context" + "fmt" + "io" + "log" + "math/rand" + "mime" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/advertisement/mapper" + "narasi-ahli-be/app/module/advertisement/repository" + "narasi-ahli-be/app/module/advertisement/request" + "narasi-ahli-be/app/module/advertisement/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +// AdvertisementService +type advertisementService struct { + Repo repository.AdvertisementRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger + Cfg *config.Config + MinioStorage *minioStorage.MinioStorage +} + +// AdvertisementService define interface of IAdvertisementService +type AdvertisementService interface { + All(req request.AdvertisementQueryRequest) (advertisement []*response.AdvertisementResponse, paging paginator.Pagination, err error) + Show(id uint) (advertisement *response.AdvertisementResponse, err error) + Save(req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) + Upload(c *fiber.Ctx, id uint) (err error) + Update(id uint, req request.AdvertisementUpdateRequest) (err error) + UpdatePublish(id uint, isPublish bool) (err error) + Delete(id uint) error + Viewer(c *fiber.Ctx) (err error) +} + +// NewAdvertisementService init AdvertisementService +func NewAdvertisementService(repo repository.AdvertisementRepository, minioStorage *minioStorage.MinioStorage, usersRepo usersRepository.UsersRepository, log zerolog.Logger, cfg *config.Config) AdvertisementService { + + return &advertisementService{ + Repo: repo, + UsersRepo: usersRepo, + MinioStorage: minioStorage, + Log: log, + Cfg: cfg, + } +} + +// All implement interface of AdvertisementService +func (_i *advertisementService) All(req request.AdvertisementQueryRequest) (advertisements []*response.AdvertisementResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + host := _i.Cfg.App.Domain + for _, result := range results { + advertisements = append(advertisements, mapper.AdvertisementResponseMapper(result, host)) + } + + return +} + +func (_i *advertisementService) Show(id uint) (advertisement *response.AdvertisementResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + host := _i.Cfg.App.Domain + return mapper.AdvertisementResponseMapper(result, host), nil +} + +func (_i *advertisementService) Save(req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + return _i.Repo.Create(newReq) +} + +func (_i *advertisementService) Upload(c *fiber.Ctx, id uint) (err error) { + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + + if err != nil { + return err + } + //filess := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + + result, err := _i.Repo.FindOne(id) + + if result == nil { + // Return status 400. Id not found. + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + for _, files := range form.File { + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top"). + Interface("files", files).Msg("") + + for _, fileHeader := range files { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop"). + Interface("data", fileHeader).Msg("") + + src, err := fileHeader.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(fileHeader.Filename) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(fileHeader.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + + objectName := fmt.Sprintf("advertisement/upload/%d/%d/%s", now.Year(), now.Month(), newFilename) + + result.ContentFileName = &newFilename + result.ContentFilePath = &objectName + + err = _i.Repo.Update(id, result) + if err != nil { + return err + } + + // Upload file ke MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + } + } + + return +} + +func (_i *advertisementService) Update(id uint, req request.AdvertisementUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + return _i.Repo.Update(id, newReq) +} + +func (_i *advertisementService) UpdatePublish(id uint, isPublish bool) (err error) { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). + Interface("ids", id).Msg("") + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). + Interface("isPublish", isPublish).Msg("") + + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + result.IsPublish = isPublish + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). + Interface("result", result).Msg("") + + return _i.Repo.Update(id, result) +} + +func (_i *advertisementService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + result.IsActive = false + return _i.Repo.Update(id, result) +} + +func (_i *advertisementService) Viewer(c *fiber.Ctx) (err error) { + filename := c.Params("filename") + result, err := _i.Repo.FindByFilename(filename) + if err != nil { + return err + } + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := *result.ContentFilePath + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + log.Fatalln(err) + } + defer fileContent.Close() + + // Tentukan Content-Type berdasarkan ekstensi file + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) + if contentType == "" { + contentType = "application/octet-stream" // fallback jika tidak ada tipe MIME yang cocok + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return +} + +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // jika tidak ada ekstensi, kembalikan string kosong + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // ambil ekstensi terakhir + return parts[len(parts)-1] +} diff --git a/app/module/ai_chat/ai_chat.module.go b/app/module/ai_chat/ai_chat.module.go new file mode 100644 index 0000000..49ed067 --- /dev/null +++ b/app/module/ai_chat/ai_chat.module.go @@ -0,0 +1,27 @@ +package ai_chat + +import ( + "narasi-ahli-be/app/module/ai_chat/controller" + "narasi-ahli-be/app/module/ai_chat/repository" + "narasi-ahli-be/app/module/ai_chat/service" + usersRepository "narasi-ahli-be/app/module/users/repository" + + "github.com/rs/zerolog" + "go.uber.org/fx" +) + +var Module = fx.Options( + fx.Provide( + repository.NewAIChatRepository, + service.NewAIChatService, + controller.NewAIChatController, + ), + fx.Invoke(func( + aiChatController controller.AIChatController, + usersRepo usersRepository.UsersRepository, + log zerolog.Logger, + ) { + log.Info().Msg("AI Chat module initialized successfully") + }), +) + diff --git a/app/module/ai_chat/controller/ai_chat.controller.go b/app/module/ai_chat/controller/ai_chat.controller.go new file mode 100644 index 0000000..698c26b --- /dev/null +++ b/app/module/ai_chat/controller/ai_chat.controller.go @@ -0,0 +1,459 @@ +package controller + +import ( + "narasi-ahli-be/app/module/ai_chat/request" + "narasi-ahli-be/app/module/ai_chat/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" +) + +type aiChatController struct { + aiChatService service.AIChatService + Log zerolog.Logger +} + +type AIChatController interface { + // AI Chat Sessions + GetUserSessions(c *fiber.Ctx) error + GetSession(c *fiber.Ctx) error + CreateSession(c *fiber.Ctx) error + UpdateSession(c *fiber.Ctx) error + DeleteSession(c *fiber.Ctx) error + + // AI Chat Messages + GetSessionMessages(c *fiber.Ctx) error + SendMessage(c *fiber.Ctx) error + UpdateMessage(c *fiber.Ctx) error + DeleteMessage(c *fiber.Ctx) error + + // AI Chat Logs + GetUserLogs(c *fiber.Ctx) error + GetLog(c *fiber.Ctx) error +} + +func NewAIChatController(aiChatService service.AIChatService, log zerolog.Logger) AIChatController { + return &aiChatController{ + aiChatService: aiChatService, + Log: log, + } +} + +// Get User Sessions +// @Summary Get user AI chat sessions +// @Description API for getting all AI chat sessions for authenticated user +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.AIChatSessionsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions [get] +func (_i *aiChatController) GetUserSessions(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + reqContext := request.AIChatSessionsQueryRequestContext{ + Status: c.Query("status"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + sessionsData, paging, err := _i.aiChatService.GetUserSessions(authHeader, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat sessions successfully retrieved"}, + Data: sessionsData, + Meta: paging, + }) +} + +// Get Session +// @Summary Get one AI chat session +// @Description API for getting one AI chat session +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Session ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{id} [get] +func (_i *aiChatController) GetSession(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + sessionData, err := _i.aiChatService.GetSession(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat session successfully retrieved"}, + Data: sessionData, + }) +} + +// Create Session +// @Summary Create AI chat session +// @Description API for create AI chat session +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.AIChatSessionsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions [post] +func (_i *aiChatController) CreateSession(c *fiber.Ctx) error { + req := new(request.AIChatSessionsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + dataResult, err := _i.aiChatService.CreateSession(authHeader, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat session successfully created"}, + Data: dataResult, + }) +} + +// Update Session +// @Summary Update AI chat session +// @Description API for update AI chat session +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Session ID" +// @Param payload body request.AIChatSessionsUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{id} [put] +func (_i *aiChatController) UpdateSession(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.AIChatSessionsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.aiChatService.UpdateSession(authHeader, uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat session successfully updated"}, + }) +} + +// Delete Session +// @Summary Delete AI chat session +// @Description API for delete AI chat session +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Session ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{id} [delete] +func (_i *aiChatController) DeleteSession(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.aiChatService.DeleteSession(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat session successfully deleted"}, + }) +} + +// Get Session Messages +// @Summary Get AI chat session messages +// @Description API for getting all messages in an AI chat session +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param sessionId path int true "Session ID" +// @Param req query request.AIChatMessagesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{sessionId}/messages [get] +func (_i *aiChatController) GetSessionMessages(c *fiber.Ctx) error { + sessionId, err := strconv.ParseUint(c.Params("sessionId"), 10, 0) + if err != nil { + return err + } + + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + reqContext := request.AIChatMessagesQueryRequestContext{ + SessionID: c.Query("sessionId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + messagesData, paging, err := _i.aiChatService.GetSessionMessages(authHeader, uint(sessionId), req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat messages successfully retrieved"}, + Data: messagesData, + Meta: paging, + }) +} + +// Send Message +// @Summary Send message to AI chat session +// @Description API for sending a message to an AI chat session +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param sessionId path int true "Session ID" +// @Param payload body request.AIChatMessagesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{sessionId}/messages [post] +func (_i *aiChatController) SendMessage(c *fiber.Ctx) error { + sessionId, err := strconv.ParseUint(c.Params("sessionId"), 10, 0) + if err != nil { + return err + } + + req := new(request.AIChatMessagesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + dataResult, err := _i.aiChatService.SendMessage(authHeader, uint(sessionId), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Message successfully sent"}, + Data: dataResult, + }) +} + +// Update Message +// @Summary Update AI chat message +// @Description API for update AI chat message +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param sessionId path int true "Session ID" +// @Param messageId path int true "Message ID" +// @Param payload body request.AIChatMessagesUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{sessionId}/messages/{messageId} [put] +func (_i *aiChatController) UpdateMessage(c *fiber.Ctx) error { + sessionId, err := strconv.ParseUint(c.Params("sessionId"), 10, 0) + if err != nil { + return err + } + + messageId, err := strconv.ParseUint(c.Params("messageId"), 10, 0) + if err != nil { + return err + } + + req := new(request.AIChatMessagesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.aiChatService.UpdateMessage(authHeader, uint(sessionId), uint(messageId), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat message successfully updated"}, + }) +} + +// Delete Message +// @Summary Delete AI chat message +// @Description API for delete AI chat message +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param sessionId path int true "Session ID" +// @Param messageId path int true "Message ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/sessions/{sessionId}/messages/{messageId} [delete] +func (_i *aiChatController) DeleteMessage(c *fiber.Ctx) error { + sessionId, err := strconv.ParseUint(c.Params("sessionId"), 10, 0) + if err != nil { + return err + } + + messageId, err := strconv.ParseUint(c.Params("messageId"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.aiChatService.DeleteMessage(authHeader, uint(sessionId), uint(messageId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat message successfully deleted"}, + }) +} + +// Get User Logs +// @Summary Get user AI chat logs +// @Description API for getting all AI chat logs for authenticated user +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.AIChatLogsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/logs [get] +func (_i *aiChatController) GetUserLogs(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + reqContext := request.AIChatLogsQueryRequestContext{ + LogType: c.Query("logType"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + logsData, paging, err := _i.aiChatService.GetUserLogs(authHeader, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat logs successfully retrieved"}, + Data: logsData, + Meta: paging, + }) +} + +// Get Log +// @Summary Get one AI chat log +// @Description API for getting one AI chat log +// @Tags AI Chat +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Log ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /ai-chat/logs/{id} [get] +func (_i *aiChatController) GetLog(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + logData, err := _i.aiChatService.GetLog(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"AI chat log successfully retrieved"}, + Data: logData, + }) +} diff --git a/app/module/ai_chat/mapper/ai_chat.mapper.go b/app/module/ai_chat/mapper/ai_chat.mapper.go new file mode 100644 index 0000000..c136659 --- /dev/null +++ b/app/module/ai_chat/mapper/ai_chat.mapper.go @@ -0,0 +1,70 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/ai_chat/response" +) + +func AIChatSessionsResponseMapper(session *entity.AIChatSessions) *response.AIChatSessionsResponse { + result := &response.AIChatSessionsResponse{ + ID: session.ID, + AISessionID: session.AISessionID, + UserID: session.UserID, + AgentID: session.AgentID, + Title: session.Title, + MessageCount: session.MessageCount, + Status: session.Status, + CreatedAt: session.CreatedAt, + UpdatedAt: session.UpdatedAt, + } + + if session.User != nil { + result.User = &response.UserBasicInfo{ + ID: session.User.ID, + Username: session.User.Username, + Fullname: session.User.Fullname, + Email: session.User.Email, + } + } + + return result +} + +func AIChatMessagesResponseMapper(message *entity.AIChatMessages) *response.AIChatMessagesResponse { + return &response.AIChatMessagesResponse{ + ID: message.ID, + SessionID: message.SessionID, + MessageType: message.MessageType, + Content: message.Content, + CreatedAt: message.CreatedAt, + } +} + +func AIChatSessionWithMessagesResponseMapper(session *entity.AIChatSessions, messages []*entity.AIChatMessages) *response.AIChatSessionWithMessagesResponse { + sessionResponse := AIChatSessionsResponseMapper(session) + + var messagesResponse []*response.AIChatMessagesResponse + for _, message := range messages { + messagesResponse = append(messagesResponse, AIChatMessagesResponseMapper(message)) + } + + return &response.AIChatSessionWithMessagesResponse{ + Session: sessionResponse, + Messages: messagesResponse, + } +} + +func AIChatLogsResponseMapper(log *entity.AIChatLogs) *response.AIChatLogsResponse { + result := &response.AIChatLogsResponse{ + ID: log.ID, + UserID: log.UserID, + SessionID: log.SessionID, + StartDate: log.StartDate, + EndDate: log.EndDate, + TotalDuration: log.TotalDuration, + CreatedAt: log.CreatedAt, + UpdatedAt: log.UpdatedAt, + } + + return result +} diff --git a/app/module/ai_chat/repository/ai_chat.repository.go b/app/module/ai_chat/repository/ai_chat.repository.go new file mode 100644 index 0000000..ed0d54b --- /dev/null +++ b/app/module/ai_chat/repository/ai_chat.repository.go @@ -0,0 +1,198 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/ai_chat/request" + "narasi-ahli-be/utils/paginator" +) + +type aiChatRepository struct { + DB *database.Database +} + +type AIChatRepository interface { + // AI Chat Sessions + GetUserSessions(userId uint, req request.AIChatSessionsQueryRequest) (sessions []*entity.AIChatSessions, paging paginator.Pagination, err error) + FindSessionByUserAndId(userId uint, sessionId uint) (session *entity.AIChatSessions, err error) + FindSessionByAISessionId(aiSessionId string) (session *entity.AIChatSessions, err error) + CreateSession(session *entity.AIChatSessions) (result *entity.AIChatSessions, err error) + UpdateSession(userId uint, sessionId uint, session *entity.AIChatSessions) (err error) + DeleteSession(userId uint, sessionId uint) (err error) + IncrementMessageCount(sessionId uint) (err error) + + // AI Chat Messages + GetSessionMessages(sessionId uint, req request.AIChatMessagesQueryRequest) (messages []*entity.AIChatMessages, paging paginator.Pagination, err error) + CreateMessage(message *entity.AIChatMessages) (result *entity.AIChatMessages, err error) + UpdateMessage(messageId uint, message *entity.AIChatMessages) (err error) + DeleteMessage(messageId uint) (err error) + GetLastMessage(sessionId uint) (message *entity.AIChatMessages, err error) + + // AI Chat Logs + GetUserLogs(userId uint, req request.AIChatLogsQueryRequest) (logs []*entity.AIChatLogs, paging paginator.Pagination, err error) + FindLogByUserAndId(userId uint, logId uint) (log *entity.AIChatLogs, err error) +} + +func NewAIChatRepository(db *database.Database) AIChatRepository { + return &aiChatRepository{ + DB: db, + } +} + +// AI Chat Sessions methods +func (_i *aiChatRepository) GetUserSessions(userId uint, req request.AIChatSessionsQueryRequest) (sessions []*entity.AIChatSessions, paging paginator.Pagination, err error) { + query := _i.DB.DB.Where("user_id = ?", userId) + + // Apply filters + if req.Status != nil { + query = query.Where("status = ?", *req.Status) + } + + // Include user relationship + query = query.Preload("User") + + // Order by updated_at desc (most recent first) + query = query.Order("updated_at DESC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&sessions).Error + paging = *req.Pagination + + return +} + +func (_i *aiChatRepository) FindSessionByUserAndId(userId uint, sessionId uint) (session *entity.AIChatSessions, err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, sessionId).Preload("User").First(&session).Error + return +} + +func (_i *aiChatRepository) FindSessionByAISessionId(aiSessionId string) (session *entity.AIChatSessions, err error) { + err = _i.DB.DB.Where("ai_session_id = ?", aiSessionId).Preload("User").First(&session).Error + return +} + +func (_i *aiChatRepository) CreateSession(session *entity.AIChatSessions) (result *entity.AIChatSessions, err error) { + err = _i.DB.DB.Create(session).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.DB.Preload("User").First(&result, session.ID).Error + return +} + +func (_i *aiChatRepository) UpdateSession(userId uint, sessionId uint, session *entity.AIChatSessions) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, sessionId).Updates(session).Error + return +} + +func (_i *aiChatRepository) DeleteSession(userId uint, sessionId uint) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, sessionId).Delete(&entity.AIChatSessions{}).Error + return +} + +func (_i *aiChatRepository) IncrementMessageCount(sessionId uint) (err error) { + err = _i.DB.DB.Exec("UPDATE ai_chat_sessions SET message_count = message_count + 1 WHERE id = ?", sessionId).Error + return +} + +// AI Chat Messages methods +func (_i *aiChatRepository) GetSessionMessages(sessionId uint, req request.AIChatMessagesQueryRequest) (messages []*entity.AIChatMessages, paging paginator.Pagination, err error) { + query := _i.DB.DB.Where("session_id = ?", sessionId) + + // Order by created_at asc (oldest first for chat) + query = query.Order("created_at ASC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&messages).Error + paging = *req.Pagination + + return +} + +func (_i *aiChatRepository) CreateMessage(message *entity.AIChatMessages) (result *entity.AIChatMessages, err error) { + err = _i.DB.DB.Create(message).Error + if err != nil { + return nil, err + } + + // Reload + err = _i.DB.DB.First(&result, message.ID).Error + return +} + +func (_i *aiChatRepository) UpdateMessage(messageId uint, message *entity.AIChatMessages) (err error) { + err = _i.DB.DB.Model(&entity.AIChatMessages{}).Where("id = ?", messageId).Updates(message).Error + return +} + +func (_i *aiChatRepository) DeleteMessage(messageId uint) (err error) { + err = _i.DB.DB.Where("id = ?", messageId).Delete(&entity.AIChatMessages{}).Error + return +} + +// AI Chat Logs methods +func (_i *aiChatRepository) GetUserLogs(userId uint, req request.AIChatLogsQueryRequest) (logs []*entity.AIChatLogs, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Where("user_id = ?", userId) + + // Note: AIChatLogs entity doesn't have LogType field, so we skip this filter + // if req.LogType != nil && *req.LogType != "" { + // query = query.Where("log_type = ?", *req.LogType) + // } + + // Count total records + err = query.Model(&entity.AIChatLogs{}).Count(&count).Error + if err != nil { + return + } + + // Apply pagination + if req.Pagination != nil { + query = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit) + } + + // Order by created_at desc (newest first) + query = query.Order("created_at DESC") + + err = query.Find(&logs).Error + if err != nil { + return + } + + // Set pagination info + if req.Pagination != nil { + paging = *req.Pagination + paging.Count = count + paging.TotalPage = int((count + int64(req.Pagination.Limit) - 1) / int64(req.Pagination.Limit)) + } + + return +} + +func (_i *aiChatRepository) FindLogByUserAndId(userId uint, logId uint) (log *entity.AIChatLogs, err error) { + query := _i.DB.DB.Where("user_id = ? AND id = ?", userId, logId) + + if err := query.First(&log).Error; err != nil { + return nil, err + } + + return +} + +func (_i *aiChatRepository) GetLastMessage(sessionId uint) (message *entity.AIChatMessages, err error) { + err = _i.DB.DB.Where("session_id = ?", sessionId).Order("created_at DESC").First(&message).Error + return +} diff --git a/app/module/ai_chat/request/ai_chat.request.go b/app/module/ai_chat/request/ai_chat.request.go new file mode 100644 index 0000000..83e3151 --- /dev/null +++ b/app/module/ai_chat/request/ai_chat.request.go @@ -0,0 +1,118 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" +) + +// AI Chat Sessions Request DTOs +type AIChatSessionsQueryRequest struct { + Status *string `json:"status"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type AIChatSessionsCreateRequest struct { + Title string `json:"title" validate:"required,min=2,max=255"` + AgentID *string `json:"agentId"` +} + +func (req AIChatSessionsCreateRequest) ToEntity() *entity.AIChatSessions { + return &entity.AIChatSessions{ + AISessionID: "", // Will be generated in service layer + AgentID: req.AgentID, + Title: req.Title, + MessageCount: 0, + Status: "active", + } +} + +type AIChatSessionsUpdateRequest struct { + Title string `json:"title" validate:"required,min=2,max=255"` + Status string `json:"status" validate:"required,oneof=active archived deleted"` +} + +func (req AIChatSessionsUpdateRequest) ToEntity() *entity.AIChatSessions { + return &entity.AIChatSessions{ + Title: req.Title, + Status: req.Status, + } +} + +// AI Chat Messages Request DTOs +type AIChatMessagesQueryRequest struct { + SessionID uint `json:"sessionId" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type AIChatMessagesCreateRequest struct { + SessionID uint `json:"sessionId" validate:"required"` + MessageType string `json:"messageType" validate:"required,oneof=user assistant"` + Content string `json:"content" validate:"required,min=1"` +} + +func (req AIChatMessagesCreateRequest) ToEntity() *entity.AIChatMessages { + return &entity.AIChatMessages{ + SessionID: req.SessionID, + MessageType: req.MessageType, + Content: req.Content, + } +} + +type AIChatMessagesUpdateRequest struct { + Content string `json:"content" validate:"required,min=1"` +} + +func (req AIChatMessagesUpdateRequest) ToEntity() *entity.AIChatMessages { + return &entity.AIChatMessages{ + Content: req.Content, + } +} + +type AIChatSessionsQueryRequestContext struct { + Status string `json:"status"` +} + +func (req AIChatSessionsQueryRequestContext) ToParamRequest() AIChatSessionsQueryRequest { + var request AIChatSessionsQueryRequest + + if status := req.Status; status != "" { + request.Status = &status + } + + return request +} + +type AIChatMessagesQueryRequestContext struct { + SessionID string `json:"sessionId"` +} + +func (req AIChatMessagesQueryRequestContext) ToParamRequest() AIChatMessagesQueryRequest { + var request AIChatMessagesQueryRequest + + if sessionIDStr := req.SessionID; sessionIDStr != "" { + // Parse session ID from string to uint + // This will be handled in the controller + } + + return request +} + +// AI Chat Logs Request DTOs +type AIChatLogsQueryRequest struct { + LogType *string `json:"logType"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type AIChatLogsQueryRequestContext struct { + LogType string `json:"logType"` +} + +func (req AIChatLogsQueryRequestContext) ToParamRequest() AIChatLogsQueryRequest { + var request AIChatLogsQueryRequest + + if logType := req.LogType; logType != "" { + request.LogType = &logType + } + + return request +} diff --git a/app/module/ai_chat/response/ai_chat.response.go b/app/module/ai_chat/response/ai_chat.response.go new file mode 100644 index 0000000..7abf617 --- /dev/null +++ b/app/module/ai_chat/response/ai_chat.response.go @@ -0,0 +1,62 @@ +package response + +import ( + "time" +) + +// AI Chat Sessions Response DTOs +type AIChatSessionsResponse struct { + ID uint `json:"id"` + AISessionID string `json:"aiSessionId"` + UserID uint `json:"userId"` + AgentID *string `json:"agentId"` + Title string `json:"title"` + MessageCount int `json:"messageCount"` + Status string `json:"status"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + // Nested user info + User *UserBasicInfo `json:"user,omitempty"` + + // Last message preview + LastMessage *AIChatMessagesResponse `json:"lastMessage,omitempty"` +} + +// AI Chat Messages Response DTOs +type AIChatMessagesResponse struct { + ID uint `json:"id"` + SessionID uint `json:"sessionId"` + MessageType string `json:"messageType"` + Content string `json:"content"` + CreatedAt time.Time `json:"createdAt"` +} + +type UserBasicInfo struct { + ID uint `json:"id"` + Username string `json:"username"` + Fullname string `json:"fullname"` + Email string `json:"email"` +} + +// Session with messages +type AIChatSessionWithMessagesResponse struct { + Session *AIChatSessionsResponse `json:"session"` + Messages []*AIChatMessagesResponse `json:"messages"` + Pagination interface{} `json:"pagination"` +} + +// AI Chat Logs Response DTOs +type AIChatLogsResponse struct { + ID uint `json:"id"` + UserID uint `json:"userId"` + SessionID uint `json:"sessionId"` + StartDate time.Time `json:"startDate"` + EndDate *time.Time `json:"endDate"` + TotalDuration int64 `json:"totalDuration"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + // Nested user info + User *UserBasicInfo `json:"user,omitempty"` +} diff --git a/app/module/ai_chat/service/ai_chat.service.go b/app/module/ai_chat/service/ai_chat.service.go new file mode 100644 index 0000000..d03eb86 --- /dev/null +++ b/app/module/ai_chat/service/ai_chat.service.go @@ -0,0 +1,246 @@ +package service + +import ( + "errors" + "fmt" + "narasi-ahli-be/app/module/ai_chat/mapper" + "narasi-ahli-be/app/module/ai_chat/repository" + "narasi-ahli-be/app/module/ai_chat/request" + "narasi-ahli-be/app/module/ai_chat/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + + "github.com/rs/zerolog" +) + +type aiChatService struct { + Repo repository.AIChatRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +type AIChatService interface { + // Sessions + GetUserSessions(authToken string, req request.AIChatSessionsQueryRequest) (sessions []*response.AIChatSessionsResponse, paging paginator.Pagination, err error) + GetSession(authToken string, sessionId uint) (session *response.AIChatSessionsResponse, err error) + CreateSession(authToken string, req request.AIChatSessionsCreateRequest) (session *response.AIChatSessionsResponse, err error) + UpdateSession(authToken string, sessionId uint, req request.AIChatSessionsUpdateRequest) (err error) + DeleteSession(authToken string, sessionId uint) error + ArchiveSession(authToken string, sessionId uint) error + + // Messages + GetSessionMessages(authToken string, sessionId uint, req request.AIChatMessagesQueryRequest) (messages []*response.AIChatMessagesResponse, paging paginator.Pagination, err error) + SendMessage(authToken string, sessionId uint, req request.AIChatMessagesCreateRequest) (message *response.AIChatMessagesResponse, err error) + UpdateMessage(authToken string, sessionId uint, messageId uint, req request.AIChatMessagesUpdateRequest) (err error) + DeleteMessage(authToken string, sessionId uint, messageId uint) error + + // Logs + GetUserLogs(authToken string, req request.AIChatLogsQueryRequest) (logs []*response.AIChatLogsResponse, paging paginator.Pagination, err error) + GetLog(authToken string, logId uint) (log *response.AIChatLogsResponse, err error) +} + +func NewAIChatService(repo repository.AIChatRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger) AIChatService { + return &aiChatService{ + Repo: repo, + UsersRepo: usersRepo, + Log: log, + } +} + +// Sessions methods +func (_i *aiChatService) GetUserSessions(authToken string, req request.AIChatSessionsQueryRequest) (sessions []*response.AIChatSessionsResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + results, paging, err := _i.Repo.GetUserSessions(userInfo.ID, req) + if err != nil { + return + } + + for _, result := range results { + sessions = append(sessions, mapper.AIChatSessionsResponseMapper(result)) + } + + return +} + +func (_i *aiChatService) GetSession(authToken string, sessionId uint) (session *response.AIChatSessionsResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + result, err := _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return nil, err + } + + return mapper.AIChatSessionsResponseMapper(result), nil +} + +func (_i *aiChatService) CreateSession(authToken string, req request.AIChatSessionsCreateRequest) (session *response.AIChatSessionsResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + entity := req.ToEntity() + entity.UserID = userInfo.ID + + // Generate unique AI session ID + entity.AISessionID = fmt.Sprintf("ai_session_%d_%d", userInfo.ID, entity.ID) + + result, err := _i.Repo.CreateSession(entity) + if err != nil { + return nil, err + } + + return mapper.AIChatSessionsResponseMapper(result), nil +} + +func (_i *aiChatService) UpdateSession(authToken string, sessionId uint, req request.AIChatSessionsUpdateRequest) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + _i.Log.Info().Interface("data", req).Msg("Updating AI chat session") + + // Check if session exists and belongs to user + existing, err := _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return err + } + if existing == nil { + return errors.New("AI chat session not found") + } + + entity := req.ToEntity() + return _i.Repo.UpdateSession(userInfo.ID, sessionId, entity) +} + +func (_i *aiChatService) DeleteSession(authToken string, sessionId uint) error { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + _i.Log.Info().Uint("userId", userInfo.ID).Uint("sessionId", sessionId).Msg("Deleting AI chat session") + + // Check if session exists and belongs to user + existing, err := _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return err + } + if existing == nil { + return errors.New("AI chat session not found") + } + + return _i.Repo.DeleteSession(userInfo.ID, sessionId) +} + +func (_i *aiChatService) ArchiveSession(authToken string, sessionId uint) error { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + _i.Log.Info().Uint("userId", userInfo.ID).Uint("sessionId", sessionId).Msg("Archiving AI chat session") + + // Check if session exists and belongs to user + existing, err := _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return err + } + if existing == nil { + return errors.New("AI chat session not found") + } + + // Update status to archived + existing.Status = "archived" + return _i.Repo.UpdateSession(userInfo.ID, sessionId, existing) +} + +// Messages methods +func (_i *aiChatService) GetSessionMessages(authToken string, sessionId uint, req request.AIChatMessagesQueryRequest) (messages []*response.AIChatMessagesResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + // Verify session belongs to user + _, err = _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return nil, paginator.Pagination{}, err + } + + results, paging, err := _i.Repo.GetSessionMessages(sessionId, req) + if err != nil { + return + } + + for _, result := range results { + messages = append(messages, mapper.AIChatMessagesResponseMapper(result)) + } + + return +} + +func (_i *aiChatService) SendMessage(authToken string, sessionId uint, req request.AIChatMessagesCreateRequest) (message *response.AIChatMessagesResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + _i.Log.Info().Interface("data", req).Msg("Sending AI chat message") + + // Verify session belongs to user + _, err = _i.Repo.FindSessionByUserAndId(userInfo.ID, req.SessionID) + if err != nil { + return nil, err + } + + entity := req.ToEntity() + + result, err := _i.Repo.CreateMessage(entity) + if err != nil { + return nil, err + } + + // Increment message count in session + err = _i.Repo.IncrementMessageCount(req.SessionID) + if err != nil { + _i.Log.Error().Err(err).Msg("Failed to increment message count") + } + + return mapper.AIChatMessagesResponseMapper(result), nil +} + +// Update Message +func (_i *aiChatService) UpdateMessage(authToken string, sessionId uint, messageId uint, req request.AIChatMessagesUpdateRequest) (err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + // Verify session belongs to user + _, err = _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return err + } + + entity := req.ToEntity() + return _i.Repo.UpdateMessage(messageId, entity) +} + +// Delete Message +func (_i *aiChatService) DeleteMessage(authToken string, sessionId uint, messageId uint) error { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + // Verify session belongs to user + _, err := _i.Repo.FindSessionByUserAndId(userInfo.ID, sessionId) + if err != nil { + return err + } + + return _i.Repo.DeleteMessage(messageId) +} + +// Logs methods +func (_i *aiChatService) GetUserLogs(authToken string, req request.AIChatLogsQueryRequest) (logs []*response.AIChatLogsResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + results, paging, err := _i.Repo.GetUserLogs(userInfo.ID, req) + if err != nil { + return + } + + for _, result := range results { + logs = append(logs, mapper.AIChatLogsResponseMapper(result)) + } + + return +} + +func (_i *aiChatService) GetLog(authToken string, logId uint) (log *response.AIChatLogsResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + result, err := _i.Repo.FindLogByUserAndId(userInfo.ID, logId) + if err != nil { + return nil, err + } + + return mapper.AIChatLogsResponseMapper(result), nil +} diff --git a/app/module/article_approvals/article_approvals.module.go b/app/module/article_approvals/article_approvals.module.go new file mode 100644 index 0000000..cbc1d5d --- /dev/null +++ b/app/module/article_approvals/article_approvals.module.go @@ -0,0 +1,54 @@ +package article_approvals + +import ( + "narasi-ahli-be/app/module/article_approvals/controller" + "narasi-ahli-be/app/module/article_approvals/repository" + "narasi-ahli-be/app/module/article_approvals/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ArticleApprovalsRouter +type ArticleApprovalsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ArticleApprovals module +var NewArticleApprovalsModule = fx.Options( + // register repository of ArticleApprovals module + fx.Provide(repository.NewArticleApprovalsRepository), + + // register service of ArticleApprovals module + fx.Provide(service.NewArticleApprovalsService), + + // register controller of ArticleApprovals module + fx.Provide(controller.NewController), + + // register router of ArticleApprovals module + fx.Provide(NewArticleApprovalsRouter), +) + +// init ArticleApprovalsRouter +func NewArticleApprovalsRouter(fiber *fiber.App, controller *controller.Controller) *ArticleApprovalsRouter { + return &ArticleApprovalsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ArticleApprovals module +func (_i *ArticleApprovalsRouter) RegisterArticleApprovalsRoutes() { + // define controllers + articleApprovalsController := _i.Controller.ArticleApprovals + + // define routes + _i.App.Route("/article-approvals", func(router fiber.Router) { + router.Get("/", articleApprovalsController.All) + router.Get("/:id", articleApprovalsController.Show) + router.Post("/", articleApprovalsController.Save) + router.Put("/:id", articleApprovalsController.Update) + router.Delete("/:id", articleApprovalsController.Delete) + }) +} diff --git a/app/module/article_approvals/controller/article_approvals.controller.go b/app/module/article_approvals/controller/article_approvals.controller.go new file mode 100644 index 0000000..8a67a0a --- /dev/null +++ b/app/module/article_approvals/controller/article_approvals.controller.go @@ -0,0 +1,203 @@ +package controller + +import ( + "narasi-ahli-be/app/module/article_approvals/request" + "narasi-ahli-be/app/module/article_approvals/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type articleApprovalsController struct { + articleApprovalsService service.ArticleApprovalsService + Log zerolog.Logger +} + +type ArticleApprovalsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewArticleApprovalsController(articleApprovalsService service.ArticleApprovalsService, log zerolog.Logger) ArticleApprovalsController { + return &articleApprovalsController{ + articleApprovalsService: articleApprovalsService, + Log: log, + } +} + +// All get all ArticleApprovals +// @Summary Get all ArticleApprovals +// @Description API for getting all ArticleApprovals +// @Tags ArticleApprovals +// @Security Bearer +// @Param req query request.ArticleApprovalsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-approvals [get] +func (_i *articleApprovalsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.ArticleApprovalsQueryRequestContext{ + ArticleId: c.Query("articleId"), + ApprovalBy: c.Query("approvalBy"), + StatusId: c.Query("statusId"), + Message: c.Query("message"), + ApprovalAtLevel: c.Query("approvalAtLevel"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + articleApprovalsData, paging, err := _i.articleApprovalsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleApprovals list successfully retrieved"}, + Data: articleApprovalsData, + Meta: paging, + }) +} + +// Show get one ArticleApprovals +// @Summary Get one ArticleApprovals +// @Description API for getting one ArticleApprovals +// @Tags ArticleApprovals +// @Security Bearer +// @Param id path int true "ArticleApprovals ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-approvals/{id} [get] +func (_i *articleApprovalsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + articleApprovalsData, err := _i.articleApprovalsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleApprovals successfully retrieved"}, + Data: articleApprovalsData, + }) +} + +// Save create ArticleApprovals +// @Summary Create ArticleApprovals +// @Description API for create ArticleApprovals +// @Tags ArticleApprovals +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ArticleApprovalsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-approvals [post] +func (_i *articleApprovalsController) Save(c *fiber.Ctx) error { + req := new(request.ArticleApprovalsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + // Get from context + dataResult, err := _i.articleApprovalsService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleApprovals successfully created"}, + Data: dataResult, + }) +} + +// Update update ArticleApprovals +// @Summary update ArticleApprovals +// @Description API for update ArticleApprovals +// @Tags ArticleApprovals +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ArticleApprovalsUpdateRequest true "Required payload" +// @Param id path int true "ArticleApprovals ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-approvals/{id} [put] +func (_i *articleApprovalsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ArticleApprovalsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.articleApprovalsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleApprovals successfully updated"}, + }) +} + +// Delete delete ArticleApprovals +// @Summary delete ArticleApprovals +// @Description API for delete ArticleApprovals +// @Tags ArticleApprovals +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "ArticleApprovals ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-approvals/{id} [delete] +func (_i *articleApprovalsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.articleApprovalsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleApprovals successfully deleted"}, + }) +} diff --git a/app/module/article_approvals/controller/controller.go b/app/module/article_approvals/controller/controller.go new file mode 100644 index 0000000..4ac0b68 --- /dev/null +++ b/app/module/article_approvals/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/article_approvals/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + ArticleApprovals ArticleApprovalsController +} + +func NewController(ArticleApprovalsService service.ArticleApprovalsService, log zerolog.Logger) *Controller { + return &Controller{ + ArticleApprovals: NewArticleApprovalsController(ArticleApprovalsService, log), + } +} diff --git a/app/module/article_approvals/mapper/article_approvals.mapper.go b/app/module/article_approvals/mapper/article_approvals.mapper.go new file mode 100644 index 0000000..2ef9306 --- /dev/null +++ b/app/module/article_approvals/mapper/article_approvals.mapper.go @@ -0,0 +1,21 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/article_approvals/response" +) + +func ArticleApprovalsResponseMapper(articleApprovalsReq *entity.ArticleApprovals) (articleApprovalsRes *res.ArticleApprovalsResponse) { + if articleApprovalsReq != nil { + articleApprovalsRes = &res.ArticleApprovalsResponse{ + ID: articleApprovalsReq.ID, + ArticleId: articleApprovalsReq.ArticleId, + ApprovalBy: articleApprovalsReq.ApprovalBy, + StatusId: articleApprovalsReq.StatusId, + Message: articleApprovalsReq.Message, + ApprovalAtLevel: articleApprovalsReq.ApprovalAtLevel, + CreatedAt: articleApprovalsReq.CreatedAt, + } + } + return articleApprovalsRes +} diff --git a/app/module/article_approvals/repository/article_approvals.repository.go b/app/module/article_approvals/repository/article_approvals.repository.go new file mode 100644 index 0000000..bf6d6f7 --- /dev/null +++ b/app/module/article_approvals/repository/article_approvals.repository.go @@ -0,0 +1,105 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_approvals/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + + "github.com/rs/zerolog" +) + +type articleApprovalsRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// ArticleApprovalsRepository define interface of IArticleApprovalsRepository +type ArticleApprovalsRepository interface { + GetAll(req request.ArticleApprovalsQueryRequest) (articleApprovalss []*entity.ArticleApprovals, paging paginator.Pagination, err error) + FindOne(id uint) (articleApprovals *entity.ArticleApprovals, err error) + Create(articleApprovals *entity.ArticleApprovals) (articleApprovalsReturn *entity.ArticleApprovals, err error) + Update(id uint, articleApprovals *entity.ArticleApprovals) (err error) + Delete(id uint) (err error) +} + +func NewArticleApprovalsRepository(db *database.Database, logger zerolog.Logger) ArticleApprovalsRepository { + return &articleApprovalsRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of IArticleApprovalsRepository +func (_i *articleApprovalsRepository) GetAll(req request.ArticleApprovalsQueryRequest) (articleApprovalss []*entity.ArticleApprovals, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.ArticleApprovals{}) + if req.ArticleId != nil { + query = query.Where("article_id = ?", req.ArticleId) + } + if req.ApprovalBy != nil { + query = query.Where("approval_by = ?", req.ApprovalBy) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + if req.Message != nil && *req.Message != "" { + message := strings.ToLower(*req.Message) + query = query.Where("LOWER(message) LIKE ?", "%"+strings.ToLower(message)+"%") + } + if req.ApprovalAtLevel != nil { + query = query.Where("approval_at_level = ?", req.ApprovalAtLevel) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articleApprovalss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *articleApprovalsRepository) FindOne(id uint) (articleApprovals *entity.ArticleApprovals, err error) { + if err := _i.DB.DB.First(&articleApprovals, id).Error; err != nil { + return nil, err + } + + return articleApprovals, nil +} + +func (_i *articleApprovalsRepository) Create(articleApprovals *entity.ArticleApprovals) (articleApprovalsReturn *entity.ArticleApprovals, err error) { + result := _i.DB.DB.Create(articleApprovals) + return articleApprovals, result.Error +} + +func (_i *articleApprovalsRepository) Update(id uint, articleApprovals *entity.ArticleApprovals) (err error) { + articleApprovalsMap, err := utilSvc.StructToMap(articleApprovals) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.ArticleApprovals{}). + Where(&entity.ArticleApprovals{ID: id}). + Updates(articleApprovalsMap).Error +} + +func (_i *articleApprovalsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.ArticleApprovals{}, id).Error +} diff --git a/app/module/article_approvals/request/article_approvals.request.go b/app/module/article_approvals/request/article_approvals.request.go new file mode 100644 index 0000000..9272d89 --- /dev/null +++ b/app/module/article_approvals/request/article_approvals.request.go @@ -0,0 +1,92 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" +) + +type ArticleApprovalsGeneric interface { + ToEntity() +} + +type ArticleApprovalsQueryRequest struct { + ArticleId *int `json:"articleId"` + ApprovalBy *int `json:"approvalBy"` + StatusId *int `json:"statusId"` + Message *string `json:"message"` + ApprovalAtLevel *int `json:"approvalAtLevel"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ArticleApprovalsCreateRequest struct { + ArticleId uint `json:"articleId" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + Message string `json:"message" validate:"required"` +} + +func (req ArticleApprovalsCreateRequest) ToEntity() *entity.ArticleApprovals { + return &entity.ArticleApprovals{ + ArticleId: req.ArticleId, + StatusId: req.StatusId, + Message: req.Message, + } +} + +type ArticleApprovalsUpdateRequest struct { + ID uint `json:"id" validate:"required"` + ArticleId uint `json:"articleId" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + Message string `json:"message" validate:"required"` +} + +func (req ArticleApprovalsUpdateRequest) ToEntity() *entity.ArticleApprovals { + return &entity.ArticleApprovals{ + ID: req.ID, + ArticleId: req.ArticleId, + StatusId: req.StatusId, + Message: req.Message, + } +} + +type ArticleApprovalsQueryRequestContext struct { + ArticleId string `json:"articleId"` + ApprovalBy string `json:"approvalBy"` + StatusId string `json:"statusId"` + Message string `json:"message"` + ApprovalAtLevel string `json:"approvalAtLevel"` +} + +func (req ArticleApprovalsQueryRequestContext) ToParamRequest() ArticleApprovalsQueryRequest { + var request ArticleApprovalsQueryRequest + + if articleIdStr := req.ArticleId; articleIdStr != "" { + articleId, err := strconv.Atoi(articleIdStr) + if err == nil { + request.ArticleId = &articleId + } + } + if approvalByStr := req.ApprovalBy; approvalByStr != "" { + approvalBy, err := strconv.Atoi(approvalByStr) + if err == nil { + request.ApprovalBy = &approvalBy + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + if message := req.Message; message != "" { + request.Message = &message + } + if approvalAtLevelStr := req.ApprovalAtLevel; approvalAtLevelStr != "" { + approvalAtLevel, err := strconv.Atoi(approvalAtLevelStr) + if err == nil { + request.ApprovalAtLevel = &approvalAtLevel + } + } + + return request +} diff --git a/app/module/article_approvals/response/article_approvals.response.go b/app/module/article_approvals/response/article_approvals.response.go new file mode 100644 index 0000000..4b56b0a --- /dev/null +++ b/app/module/article_approvals/response/article_approvals.response.go @@ -0,0 +1,13 @@ +package response + +import "time" + +type ArticleApprovalsResponse struct { + ID uint `json:"id"` + ArticleId uint `json:"articleId"` + ApprovalBy uint `json:"approvalBy"` + StatusId int `json:"statusId"` + Message string `json:"message"` + ApprovalAtLevel *int `json:"approvalAtLevel"` + CreatedAt time.Time `json:"createdAt"` +} diff --git a/app/module/article_approvals/service/article_approvals.service.go b/app/module/article_approvals/service/article_approvals.service.go new file mode 100644 index 0000000..1e9b7b8 --- /dev/null +++ b/app/module/article_approvals/service/article_approvals.service.go @@ -0,0 +1,96 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_approvals/mapper" + "narasi-ahli-be/app/module/article_approvals/repository" + "narasi-ahli-be/app/module/article_approvals/request" + "narasi-ahli-be/app/module/article_approvals/response" + articlesService "narasi-ahli-be/app/module/articles/service" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" + + utilSvc "narasi-ahli-be/utils/service" +) + +// ArticleApprovalsService +type articleApprovalsService struct { + Repo repository.ArticleApprovalsRepository + UsersRepo usersRepository.UsersRepository + ArticlesService articlesService.ArticlesService + Log zerolog.Logger +} + +// ArticleApprovalsService define interface of IArticleApprovalsService +type ArticleApprovalsService interface { + All(req request.ArticleApprovalsQueryRequest) (articleApprovals []*response.ArticleApprovalsResponse, paging paginator.Pagination, err error) + Show(id uint) (articleApprovals *response.ArticleApprovalsResponse, err error) + Save(req request.ArticleApprovalsCreateRequest, authToken string) (articleApprovals *entity.ArticleApprovals, err error) + Update(id uint, req request.ArticleApprovalsUpdateRequest) (err error) + Delete(id uint) error +} + +// NewArticleApprovalsService init ArticleApprovalsService +func NewArticleApprovalsService(repo repository.ArticleApprovalsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository, articlesSvc articlesService.ArticlesService) ArticleApprovalsService { + + return &articleApprovalsService{ + Repo: repo, + Log: log, + UsersRepo: usersRepo, + ArticlesService: articlesSvc, + } +} + +// All implement interface of ArticleApprovalsService +func (_i *articleApprovalsService) All(req request.ArticleApprovalsQueryRequest) (articleApprovalss []*response.ArticleApprovalsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + articleApprovalss = append(articleApprovalss, mapper.ArticleApprovalsResponseMapper(result)) + } + + return +} + +func (_i *articleApprovalsService) Show(id uint) (articleApprovals *response.ArticleApprovalsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.ArticleApprovalsResponseMapper(result), nil +} + +func (_i *articleApprovalsService) Save(req request.ArticleApprovalsCreateRequest, authToken string) (articleApprovals *entity.ArticleApprovals, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + newReq := req.ToEntity() + + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + newReq.ApprovalBy = createdBy.ID + newReq.ApprovalAtLevel = &createdBy.UserLevel.LevelNumber + + approvalByUserLevelId := createdBy.UserLevelId + approvalParentLevelId := createdBy.UserLevel.ParentLevelId + + err = _i.ArticlesService.UpdateApproval(newReq.ArticleId, newReq.StatusId, int(approvalByUserLevelId), *newReq.ApprovalAtLevel, *approvalParentLevelId) + if err != nil { + return nil, err + } + + return _i.Repo.Create(newReq) +} + +func (_i *articleApprovalsService) Update(id uint, req request.ArticleApprovalsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *articleApprovalsService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/article_categories/article_categories.module.go b/app/module/article_categories/article_categories.module.go new file mode 100644 index 0000000..83b5cf3 --- /dev/null +++ b/app/module/article_categories/article_categories.module.go @@ -0,0 +1,58 @@ +package article_categories + +import ( + "narasi-ahli-be/app/module/article_categories/controller" + "narasi-ahli-be/app/module/article_categories/repository" + "narasi-ahli-be/app/module/article_categories/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ArticleCategoriesRouter +type ArticleCategoriesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ArticleCategories module +var NewArticleCategoriesModule = fx.Options( + // register repository of ArticleCategories module + fx.Provide(repository.NewArticleCategoriesRepository), + + // register service of ArticleCategories module + fx.Provide(service.NewArticleCategoriesService), + + // register controller of ArticleCategories module + fx.Provide(controller.NewController), + + // register router of ArticleCategories module + fx.Provide(NewArticleCategoriesRouter), +) + +// init ArticleCategoriesRouter +func NewArticleCategoriesRouter(fiber *fiber.App, controller *controller.Controller) *ArticleCategoriesRouter { + return &ArticleCategoriesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ArticleCategories module +func (_i *ArticleCategoriesRouter) RegisterArticleCategoriesRoutes() { + // define controllers + articleCategoriesController := _i.Controller.ArticleCategories + + // define routes + _i.App.Route("/article-categories", func(router fiber.Router) { + router.Get("/", articleCategoriesController.All) + router.Get("/:id", articleCategoriesController.Show) + router.Get("/old/:id", articleCategoriesController.ShowByOldId) + router.Get("/slug/:slug", articleCategoriesController.ShowBySlug) + router.Post("/", articleCategoriesController.Save) + router.Put("/:id", articleCategoriesController.Update) + router.Post("/thumbnail/:id", articleCategoriesController.SaveThumbnail) + router.Get("/thumbnail/viewer/:id", articleCategoriesController.Viewer) + router.Delete("/:id", articleCategoriesController.Delete) + }) +} diff --git a/app/module/article_categories/controller/article_categories.controller.go b/app/module/article_categories/controller/article_categories.controller.go new file mode 100644 index 0000000..f0ebc9a --- /dev/null +++ b/app/module/article_categories/controller/article_categories.controller.go @@ -0,0 +1,307 @@ +package controller + +import ( + "narasi-ahli-be/app/module/article_categories/request" + "narasi-ahli-be/app/module/article_categories/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type articleCategoriesController struct { + articleCategoriesService service.ArticleCategoriesService +} + +type ArticleCategoriesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + ShowByOldId(c *fiber.Ctx) error + ShowBySlug(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + SaveThumbnail(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error +} + +func NewArticleCategoriesController(articleCategoriesService service.ArticleCategoriesService) ArticleCategoriesController { + return &articleCategoriesController{ + articleCategoriesService: articleCategoriesService, + } +} + +// All ArticleCategories +// @Summary Get all ArticleCategories +// @Description API for getting all ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param req query request.ArticleCategoriesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories [get] +func (_i *articleCategoriesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authToken := c.Get("Authorization") + reqContext := request.ArticleCategoriesQueryRequestContext{ + Title: c.Query("title"), + Description: c.Query("description"), + ParentId: c.Query("parentId"), + IsPublish: c.Query("isPublish"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + articleCategoriesData, paging, err := _i.articleCategoriesService.All(req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories list successfully retrieved"}, + Data: articleCategoriesData, + Meta: paging, + }) +} + +// Show ArticleCategories +// @Summary Get one ArticleCategories +// @Description API for getting one ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "ArticleCategories ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/{id} [get] +func (_i *articleCategoriesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + articleCategoriesData, err := _i.articleCategoriesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories successfully retrieved"}, + Data: articleCategoriesData, + }) +} + +// ShowByOldId ArticleCategories +// @Summary Get one ArticleCategories +// @Description API for getting one ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "ArticleCategories Old ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/old/{id} [get] +func (_i *articleCategoriesController) ShowByOldId(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + articleCategoriesData, err := _i.articleCategoriesService.ShowByOldId(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories successfully retrieved"}, + Data: articleCategoriesData, + }) +} + +// ShowBySlug ArticleCategories +// @Summary Get one ArticleCategories +// @Description API for getting one ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param slug path string true "ArticleCategories Slug" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/slug/{slug} [get] +func (_i *articleCategoriesController) ShowBySlug(c *fiber.Ctx) error { + slug := c.Params("slug") + articleCategoriesData, err := _i.articleCategoriesService.ShowBySlug(slug) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories successfully retrieved"}, + Data: articleCategoriesData, + }) +} + +// Save ArticleCategories +// @Summary Create ArticleCategories +// @Description API for create ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ArticleCategoriesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories [post] +func (_i *articleCategoriesController) Save(c *fiber.Ctx) error { + req := new(request.ArticleCategoriesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + dataResult, err := _i.articleCategoriesService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories successfully created"}, + Data: dataResult, + }) +} + +// SaveThumbnail ArticleCategories +// @Summary Upload ArticleCategories Thumbnail +// @Description API for Upload ArticleCategories Thumbnail +// @Tags Article Categories +// @Security Bearer +// @Produce json +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param files formData file true "Upload thumbnail" +// @Param id path int true "ArticleCategories ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/thumbnail/{id} [post] +func (_i *articleCategoriesController) SaveThumbnail(c *fiber.Ctx) error { + err := _i.articleCategoriesService.SaveThumbnail(c) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Thumbnail of ArticleCategories successfully created"}, + }) +} + +// Update ArticleCategories +// @Summary update ArticleCategories +// @Description API for update ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ArticleCategoriesUpdateRequest true "Required payload" +// @Param id path int true "ArticleCategories ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/{id} [put] +func (_i *articleCategoriesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ArticleCategoriesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.articleCategoriesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories successfully updated"}, + }) +} + +// Delete ArticleCategories +// @Summary delete ArticleCategories +// @Description API for delete ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "ArticleCategories ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/{id} [delete] +func (_i *articleCategoriesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.articleCategoriesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleCategories successfully deleted"}, + }) +} + +// Viewer ArticleCategories +// @Summary Viewer ArticleCategories +// @Description API for View Thumbnail of ArticleCategories +// @Tags Article Categories +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path string true "ArticleCategories ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-categories/thumbnail/viewer/{id} [get] +func (_i *articleCategoriesController) Viewer(c *fiber.Ctx) error { + return _i.articleCategoriesService.Viewer(c) +} diff --git a/app/module/article_categories/controller/controller.go b/app/module/article_categories/controller/controller.go new file mode 100644 index 0000000..3bf141b --- /dev/null +++ b/app/module/article_categories/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/article_categories/service" + +type Controller struct { + ArticleCategories ArticleCategoriesController +} + +func NewController(ArticleCategoriesService service.ArticleCategoriesService) *Controller { + return &Controller{ + ArticleCategories: NewArticleCategoriesController(ArticleCategoriesService), + } +} diff --git a/app/module/article_categories/mapper/article_categories.mapper.go b/app/module/article_categories/mapper/article_categories.mapper.go new file mode 100644 index 0000000..d05d9a5 --- /dev/null +++ b/app/module/article_categories/mapper/article_categories.mapper.go @@ -0,0 +1,39 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/article_categories/response" + "strconv" + "strings" +) + +func ArticleCategoriesResponseMapper(articleCategoriesReq *entity.ArticleCategories, host string) (articleCategoriesRes *res.ArticleCategoriesResponse) { + if articleCategoriesReq != nil { + articleCategoriesRes = &res.ArticleCategoriesResponse{ + ID: articleCategoriesReq.ID, + Title: articleCategoriesReq.Title, + Description: articleCategoriesReq.Description, + Slug: articleCategoriesReq.Slug, + ThumbnailPath: articleCategoriesReq.ThumbnailPath, + ParentId: articleCategoriesReq.ParentId, + OldCategoryId: articleCategoriesReq.OldCategoryId, + CreatedById: articleCategoriesReq.CreatedById, + StatusId: articleCategoriesReq.StatusId, + IsPublish: articleCategoriesReq.IsPublish, + PublishedAt: articleCategoriesReq.PublishedAt, + IsActive: articleCategoriesReq.IsActive, + CreatedAt: articleCategoriesReq.CreatedAt, + UpdatedAt: articleCategoriesReq.UpdatedAt, + } + + if articleCategoriesReq.Tags != nil { + tagsValue := *articleCategoriesReq.Tags + articleCategoriesRes.Tags = strings.Split(tagsValue, ",") + } + + if articleCategoriesRes.ThumbnailPath != nil { + articleCategoriesRes.ThumbnailUrl = host + "/article-categories/thumbnail/viewer/" + strconv.Itoa(int(articleCategoriesReq.ID)) + } + } + return articleCategoriesRes +} diff --git a/app/module/article_categories/repository/article_categories.repository.go b/app/module/article_categories/repository/article_categories.repository.go new file mode 100644 index 0000000..a722165 --- /dev/null +++ b/app/module/article_categories/repository/article_categories.repository.go @@ -0,0 +1,141 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_categories/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + + "github.com/rs/zerolog" +) + +type articleCategoriesRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// ArticleCategoriesRepository define interface of IArticleCategoriesRepository +type ArticleCategoriesRepository interface { + GetAll(req request.ArticleCategoriesQueryRequest) (articleCategoriess []*entity.ArticleCategories, paging paginator.Pagination, err error) + FindOne(id uint) (articleCategories *entity.ArticleCategories, err error) + FindOneByOldId(id uint) (articleCategories *entity.ArticleCategories, err error) + FindOneBySlug(slug string) (articleCategories *entity.ArticleCategories, err error) + Create(articleCategories *entity.ArticleCategories) (articleCategoriesReturn *entity.ArticleCategories, err error) + Update(id uint, articleCategories *entity.ArticleCategories) (err error) + Delete(id uint) (err error) +} + +func NewArticleCategoriesRepository(db *database.Database, log zerolog.Logger) ArticleCategoriesRepository { + return &articleCategoriesRepository{ + DB: db, + Log: log, + } +} + +// implement interface of IArticleCategoriesRepository +func (_i *articleCategoriesRepository) GetAll(req request.ArticleCategoriesQueryRequest) (articleCategoriess []*entity.ArticleCategories, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.ArticleCategories{}) + + if req.UserLevelId != nil { + query = _i.DB.DB.Model(&entity.ArticleCategories{}). + Joins("LEFT JOIN users ON article_categories.created_by_id = users.id"). + Joins("LEFT JOIN user_levels ON users.user_level_id = user_levels.id"). + Where("user_levels.id = ? or user_levels.parent_level_id = ?", *req.UserLevelId, *req.UserLevelId) + } + + query = query.Where("article_categories.is_active = ?", true) + + if req.Title != nil && *req.Title != "" { + title := strings.ToLower(*req.Title) + query = query.Where("LOWER(article_categories.title) LIKE ?", "%"+strings.ToLower(title)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(article_categories.description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.ParentId != nil { + query = query.Where("article_categories.parent_id = ?", req.ParentId) + } + if req.IsPublish != nil { + query = query.Where("article_categories.is_publish = ?", req.IsPublish) + } + if req.StatusId != nil { + query = query.Where("article_categories.status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articleCategoriess).Error + + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *articleCategoriesRepository) FindOne(id uint) (articleCategories *entity.ArticleCategories, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.First(&articleCategories).Error; err != nil { + return nil, err + } + + return articleCategories, nil +} + +func (_i *articleCategoriesRepository) FindOneByOldId(id uint) (articleCategories *entity.ArticleCategories, err error) { + query := _i.DB.DB.Where("old_category_id = ?", id) + + if err := query.First(&articleCategories).Error; err != nil { + return nil, err + } + + return articleCategories, nil +} + +func (_i *articleCategoriesRepository) FindOneBySlug(slug string) (articleCategories *entity.ArticleCategories, err error) { + query := _i.DB.DB.Where("slug = ?", slug) + + if err := query.First(&articleCategories).Error; err != nil { + return nil, err + } + + return articleCategories, nil +} + +func (_i *articleCategoriesRepository) Create(articleCategories *entity.ArticleCategories) (articleCategoriesReturn *entity.ArticleCategories, err error) { + result := _i.DB.DB.Create(articleCategories) + return articleCategories, result.Error +} + +func (_i *articleCategoriesRepository) Update(id uint, articleCategories *entity.ArticleCategories) (err error) { + articleCategoriesMap, err := utilSvc.StructToMap(articleCategories) + if err != nil { + return err + } + query := _i.DB.DB.Model(&entity.ArticleCategories{}).Where(&entity.ArticleCategories{ID: id}) + return query.Updates(articleCategoriesMap).Error +} + +func (_i *articleCategoriesRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&entity.ArticleCategories{}).Where("id = ?", id) + return query.Delete(&entity.ArticleCategories{}).Error +} diff --git a/app/module/article_categories/request/article_categories.request.go b/app/module/article_categories/request/article_categories.request.go new file mode 100644 index 0000000..6549c57 --- /dev/null +++ b/app/module/article_categories/request/article_categories.request.go @@ -0,0 +1,128 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type ArticleCategoriesGeneric interface { + ToEntity() +} + +type ArticleCategoriesQueryRequest struct { + Title *string `json:"title"` + Description *string `json:"description"` + UserLevelId *uint `json:"UserLevelId"` + UserLevelNumber *int `json:"UserLevelNumber"` + ParentId *int `json:"parentId"` + StatusId *int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ArticleCategoriesCreateRequest struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + Tags *string `json:"tags"` + Slug *string `json:"slug"` + CreatedById *uint `json:"createdById"` + ParentId *int `json:"parentId"` + OldCategoryId *uint `json:"oldCategoryId"` +} + +func (req ArticleCategoriesCreateRequest) ToEntity() *entity.ArticleCategories { + return &entity.ArticleCategories{ + Title: req.Title, + Description: req.Description, + Tags: req.Tags, + ParentId: req.ParentId, + Slug: req.Slug, + OldCategoryId: req.OldCategoryId, + StatusId: req.StatusId, + } +} + +type ArticleCategoriesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + Tags *string `json:"tags"` + Slug *string `json:"slug"` + ParentId *int `json:"parentId"` + CreatedById *uint `json:"createdById"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` +} + +func (req ArticleCategoriesUpdateRequest) ToEntity() *entity.ArticleCategories { + return &entity.ArticleCategories{ + ID: req.ID, + Title: req.Title, + Description: req.Description, + ParentId: req.ParentId, + Slug: req.Slug, + Tags: req.Tags, + StatusId: req.StatusId, + IsPublish: req.IsPublish, + PublishedAt: req.PublishedAt, + UpdatedAt: time.Now(), + } +} + +type ArticleCategoriesQueryRequestContext struct { + Title string `json:"title"` + Description string `json:"description"` + ParentId string `json:"parentId"` + StatusId string `json:"statusId"` + IsPublish string `json:"isPublish"` + UserLevelId string `json:"UserLevelId"` + UserLevelNumber string `json:"UserLevelNumber"` +} + +func (req ArticleCategoriesQueryRequestContext) ToParamRequest() ArticleCategoriesQueryRequest { + var request ArticleCategoriesQueryRequest + + if title := req.Title; title != "" { + request.Title = &title + } + if description := req.Description; description != "" { + request.Description = &description + } + if parentIdStr := req.ParentId; parentIdStr != "" { + parentId, err := strconv.Atoi(parentIdStr) + if err == nil { + request.ParentId = &parentId + } + } + if isPublishStr := req.IsPublish; isPublishStr != "" { + isPublish, err := strconv.ParseBool(isPublishStr) + if err == nil { + request.IsPublish = &isPublish + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + if userLevelIdStr := req.UserLevelId; userLevelIdStr != "" { + userLevelId, err := strconv.Atoi(userLevelIdStr) + if err == nil { + userLevelIdUint := uint(userLevelId) + request.UserLevelId = &userLevelIdUint + } + } + if userLevelNumberStr := req.UserLevelNumber; userLevelNumberStr != "" { + userLevelNumber, err := strconv.Atoi(userLevelNumberStr) + if err == nil { + request.UserLevelNumber = &userLevelNumber + } + } + + return request +} diff --git a/app/module/article_categories/response/article_categories.response.go b/app/module/article_categories/response/article_categories.response.go new file mode 100644 index 0000000..27c012e --- /dev/null +++ b/app/module/article_categories/response/article_categories.response.go @@ -0,0 +1,23 @@ +package response + +import "time" + +type ArticleCategoriesResponse struct { + ID uint `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + ThumbnailUrl string `json:"thumbnailUrl"` + Slug *string `json:"slug"` + Tags []string `json:"tags"` + ThumbnailPath *string `json:"thumbnailPath"` + ParentId *int `json:"parentId"` + OldCategoryId *uint `json:"oldCategoryId"` + CreatedById *uint `json:"createdById"` + StatusId int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` + IsEnabled *bool `json:"isEnabled"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/article_categories/service/article_categories.service.go b/app/module/article_categories/service/article_categories.service.go new file mode 100644 index 0000000..54cf844 --- /dev/null +++ b/app/module/article_categories/service/article_categories.service.go @@ -0,0 +1,281 @@ +package service + +import ( + "context" + "io" + "log" + "math/rand" + "mime" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_categories/mapper" + "narasi-ahli-be/app/module/article_categories/repository" + "narasi-ahli-be/app/module/article_categories/request" + "narasi-ahli-be/app/module/article_categories/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +// ArticleCategoriesService +type articleCategoriesService struct { + Repo repository.ArticleCategoriesRepository + UsersRepo usersRepository.UsersRepository + MinioStorage *minioStorage.MinioStorage + Log zerolog.Logger + Cfg *config.Config +} + +// ArticleCategoriesService define interface of IArticleCategoriesService +type ArticleCategoriesService interface { + All(req request.ArticleCategoriesQueryRequest, authToken string) (articleCategories []*response.ArticleCategoriesResponse, paging paginator.Pagination, err error) + Show(id uint) (articleCategories *response.ArticleCategoriesResponse, err error) + ShowByOldId(id uint) (articleCategories *response.ArticleCategoriesResponse, err error) + ShowBySlug(slug string) (articleCategories *response.ArticleCategoriesResponse, err error) + Save(req request.ArticleCategoriesCreateRequest, authToken string) (articleCategories *entity.ArticleCategories, err error) + SaveThumbnail(c *fiber.Ctx) (err error) + Update(id uint, req request.ArticleCategoriesUpdateRequest) (err error) + Delete(id uint) error + Viewer(c *fiber.Ctx) error +} + +// NewArticleCategoriesService init ArticleCategoriesService +func NewArticleCategoriesService(repo repository.ArticleCategoriesRepository, usersRepo usersRepository.UsersRepository, minioStorage *minioStorage.MinioStorage, log zerolog.Logger, cfg *config.Config) ArticleCategoriesService { + + return &articleCategoriesService{ + Repo: repo, + UsersRepo: usersRepo, + MinioStorage: minioStorage, + Log: log, + Cfg: cfg, + } +} + +// All implement interface of ArticleCategoriesService +func (_i *articleCategoriesService) All(req request.ArticleCategoriesQueryRequest, authToken string) (articleCategoriess []*response.ArticleCategoriesResponse, paging paginator.Pagination, err error) { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if createdBy != nil { + if createdBy.UserLevel.LevelNumber > 1 { + req.UserLevelId = &createdBy.UserLevelId + } + } + + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + host := _i.Cfg.App.Domain + for _, result := range results { + articleCategoriess = append(articleCategoriess, mapper.ArticleCategoriesResponseMapper(result, host)) + } + + return +} + +func (_i *articleCategoriesService) Show(id uint) (articleCategories *response.ArticleCategoriesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + host := _i.Cfg.App.Domain + return mapper.ArticleCategoriesResponseMapper(result, host), nil +} + +func (_i *articleCategoriesService) ShowByOldId(id uint) (articleCategories *response.ArticleCategoriesResponse, err error) { + result, err := _i.Repo.FindOneByOldId(id) + if err != nil { + return nil, err + } + host := _i.Cfg.App.Domain + return mapper.ArticleCategoriesResponseMapper(result, host), nil +} + +func (_i *articleCategoriesService) ShowBySlug(slug string) (articleCategories *response.ArticleCategoriesResponse, err error) { + result, err := _i.Repo.FindOneBySlug(slug) + if err != nil { + return nil, err + } + host := _i.Cfg.App.Domain + return mapper.ArticleCategoriesResponseMapper(result, host), nil +} + +func (_i *articleCategoriesService) Save(req request.ArticleCategoriesCreateRequest, authToken string) (articleCategories *entity.ArticleCategories, err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + if req.CreatedById != nil { + createdBy, err := _i.UsersRepo.FindOne(*req.CreatedById) + if err != nil { + return nil, err + } + newReq.CreatedById = &createdBy.ID + } else { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + newReq.CreatedById = &createdBy.ID + } + + return _i.Repo.Create(newReq) +} + +func (_i *articleCategoriesService) SaveThumbnail(c *fiber.Ctx) (err error) { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + if err != nil { + return err + } + files := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + // Iterasi semua file yang diunggah + for _, file := range files { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop1"). + Interface("data", file).Msg("") + + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(file.Filename) + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(file.Filename)[1:] + + rand.New(rand.NewSource(time.Now().UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + objectName := "articles/category/thumbnail/" + newFilename + + findCategory, err := _i.Repo.FindOne(uint(id)) + findCategory.ThumbnailPath = &objectName + err = _i.Repo.Update(uint(id), findCategory) + if err != nil { + return err + } + + // Upload file ke MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, file.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + } + + return +} + +func (_i *articleCategoriesService) Update(id uint, req request.ArticleCategoriesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + newReq := req.ToEntity() + if req.CreatedById != nil { + createdBy, err := _i.UsersRepo.FindOne(*req.CreatedById) + if err != nil { + return err + } + newReq.CreatedById = &createdBy.ID + } + + return _i.Repo.Update(id, newReq) +} + +func (_i *articleCategoriesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + + return _i.Repo.Update(id, result) +} + +func (_i *articleCategoriesService) Viewer(c *fiber.Ctx) (err error) { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + result, err := _i.Repo.FindOne(uint(id)) + if err != nil { + return err + } + + if result.ThumbnailPath == nil { + return nil + } + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := result.ThumbnailPath + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads"). + Interface("data", objectName).Msg("") + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + fileContent, err := minioClient.GetObject(ctx, bucketName, *objectName, minio.GetObjectOptions{}) + if err != nil { + log.Fatalln(err) + } + defer fileContent.Close() + + contentType := mime.TypeByExtension("." + getFileExtension(*objectName)) + if contentType == "" { + contentType = "application/octet-stream" + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return +} + +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // jika tidak ada ekstensi, kembalikan string kosong + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // ambil ekstensi terakhir + return parts[len(parts)-1] +} diff --git a/app/module/article_category_details/article_category_details.module.go b/app/module/article_category_details/article_category_details.module.go new file mode 100644 index 0000000..b0b1441 --- /dev/null +++ b/app/module/article_category_details/article_category_details.module.go @@ -0,0 +1,54 @@ +package article_category_details + +import ( + "narasi-ahli-be/app/module/article_category_details/controller" + "narasi-ahli-be/app/module/article_category_details/repository" + "narasi-ahli-be/app/module/article_category_details/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ArticleCategoryDetailsRouter +type ArticleCategoryDetailsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ArticleCategoryDetails module +var NewArticleCategoryDetailsModule = fx.Options( + // register repository of ArticleCategoryDetails module + fx.Provide(repository.NewArticleCategoryDetailsRepository), + + // register service of ArticleCategoryDetails module + fx.Provide(service.NewArticleCategoryDetailsService), + + // register controller of ArticleCategoryDetails module + fx.Provide(controller.NewController), + + // register router of ArticleCategoryDetails module + fx.Provide(NewArticleCategoryDetailsRouter), +) + +// init ArticleCategoryDetailsRouter +func NewArticleCategoryDetailsRouter(fiber *fiber.App, controller *controller.Controller) *ArticleCategoryDetailsRouter { + return &ArticleCategoryDetailsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ArticleCategoryDetails module +func (_i *ArticleCategoryDetailsRouter) RegisterArticleCategoryDetailsRoutes() { + // define controllers + articleCategoryDetailsController := _i.Controller.ArticleCategoryDetails + + // define routes + _i.App.Route("/article-category-details", func(router fiber.Router) { + router.Get("/", articleCategoryDetailsController.All) + router.Get("/:id", articleCategoryDetailsController.Show) + router.Post("/", articleCategoryDetailsController.Save) + router.Put("/:id", articleCategoryDetailsController.Update) + router.Delete("/:id", articleCategoryDetailsController.Delete) + }) +} diff --git a/app/module/article_category_details/controller/article_category_details.controller.go b/app/module/article_category_details/controller/article_category_details.controller.go new file mode 100644 index 0000000..9b43182 --- /dev/null +++ b/app/module/article_category_details/controller/article_category_details.controller.go @@ -0,0 +1,185 @@ +package controller + +import ( + "narasi-ahli-be/app/module/article_category_details/request" + "narasi-ahli-be/app/module/article_category_details/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type articleCategoryDetailsController struct { + articleCategoryDetailsService service.ArticleCategoryDetailsService +} + +type ArticleCategoryDetailsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewArticleCategoryDetailsController(articleCategoryDetailsService service.ArticleCategoryDetailsService) ArticleCategoryDetailsController { + return &articleCategoryDetailsController{ + articleCategoryDetailsService: articleCategoryDetailsService, + } +} + +// All get all ArticleCategoryDetails +// @Summary Get all ArticleCategoryDetails +// @Description API for getting all ArticleCategoryDetails +// @Tags Untags +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /article-category-details [get] +func (_i *articleCategoryDetailsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + var req request.ArticleCategoryDetailsQueryRequest + req.Pagination = paginate + + articleCategoryDetailsData, paging, err := _i.articleCategoryDetailsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"ArticleCategoryDetails list successfully retrieved"}, + Data: articleCategoryDetailsData, + Meta: paging, + }) +} + +// Show get one ArticleCategoryDetails +// @Summary Get one ArticleCategoryDetails +// @Description API for getting one ArticleCategoryDetails +// @Tags Untags +// @Security Bearer +// @Param id path int true "ArticleCategoryDetails ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /article-category-details/{id} [get] +func (_i *articleCategoryDetailsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + articleCategoryDetailsData, err := _i.articleCategoryDetailsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"ArticleCategoryDetails successfully retrieved"}, + Data: articleCategoryDetailsData, + }) +} + +// Save create ArticleCategoryDetails +// @Summary Create ArticleCategoryDetails +// @Description API for create ArticleCategoryDetails +// @Tags Untags +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Body request.ArticleCategoryDetailsCreateRequest +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /article-category-details [post] +func (_i *articleCategoryDetailsController) Save(c *fiber.Ctx) error { + req := new(request.ArticleCategoryDetailsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.articleCategoryDetailsService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"ArticleCategoryDetails successfully created"}, + }) +} + +// Update update ArticleCategoryDetails +// @Summary update ArticleCategoryDetails +// @Description API for update ArticleCategoryDetails +// @Tags Untags +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Body request.ArticleCategoryDetailsUpdateRequest +// @Param id path int true "ArticleCategoryDetails ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /article-category-details/{id} [put] +func (_i *articleCategoryDetailsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ArticleCategoryDetailsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.articleCategoryDetailsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"ArticleCategoryDetails successfully updated"}, + }) +} + +// Delete delete ArticleCategoryDetails +// @Summary delete ArticleCategoryDetails +// @Description API for delete ArticleCategoryDetails +// @Tags Untags +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "ArticleCategoryDetails ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /article-category-details/{id} [delete] +func (_i *articleCategoryDetailsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.articleCategoryDetailsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"ArticleCategoryDetails successfully deleted"}, + }) +} diff --git a/app/module/article_category_details/controller/controller.go b/app/module/article_category_details/controller/controller.go new file mode 100644 index 0000000..7400eb9 --- /dev/null +++ b/app/module/article_category_details/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/article_category_details/service" + +type Controller struct { + ArticleCategoryDetails ArticleCategoryDetailsController +} + +func NewController(ArticleCategoryDetailsService service.ArticleCategoryDetailsService) *Controller { + return &Controller{ + ArticleCategoryDetails: NewArticleCategoryDetailsController(ArticleCategoryDetailsService), + } +} diff --git a/app/module/article_category_details/mapper/article_category_details.mapper.go b/app/module/article_category_details/mapper/article_category_details.mapper.go new file mode 100644 index 0000000..dd0ff4c --- /dev/null +++ b/app/module/article_category_details/mapper/article_category_details.mapper.go @@ -0,0 +1,21 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity/article_category_details" + res "narasi-ahli-be/app/module/article_category_details/response" +) + +func ArticleCategoryDetailsResponseMapper(articleCategoryDetailsReq *article_category_details.ArticleCategoryDetails) (articleCategoryDetailsRes *res.ArticleCategoryDetailsResponse) { + if articleCategoryDetailsReq != nil { + + articleCategoryDetailsRes = &res.ArticleCategoryDetailsResponse{ + ID: articleCategoryDetailsReq.ID, + ArticleId: articleCategoryDetailsReq.ArticleId, + CategoryId: articleCategoryDetailsReq.CategoryId, + IsActive: articleCategoryDetailsReq.IsActive, + CreatedAt: articleCategoryDetailsReq.CreatedAt, + UpdatedAt: articleCategoryDetailsReq.UpdatedAt, + } + } + return articleCategoryDetailsRes +} diff --git a/app/module/article_category_details/repository/article_category_details.repository.go b/app/module/article_category_details/repository/article_category_details.repository.go new file mode 100644 index 0000000..3d63d8b --- /dev/null +++ b/app/module/article_category_details/repository/article_category_details.repository.go @@ -0,0 +1,78 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity/article_category_details" + "narasi-ahli-be/app/module/article_category_details/request" + "narasi-ahli-be/utils/paginator" +) + +type articleCategoryDetailsRepository struct { + DB *database.Database +} + +// ArticleCategoryDetailsRepository define interface of IArticleCategoryDetailsRepository +type ArticleCategoryDetailsRepository interface { + GetAll(req request.ArticleCategoryDetailsQueryRequest) (articleCategoryDetailss []*article_category_details.ArticleCategoryDetails, paging paginator.Pagination, err error) + FindOne(id uint) (articleCategoryDetails *article_category_details.ArticleCategoryDetails, err error) + FindByArticleId(articleId uint) (articleCategoryDetailss []*article_category_details.ArticleCategoryDetails, err error) + Create(articleCategoryDetails *article_category_details.ArticleCategoryDetails) (err error) + Update(id uint, articleCategoryDetails *article_category_details.ArticleCategoryDetails) (err error) + Delete(id uint) (err error) +} + +func NewArticleCategoryDetailsRepository(db *database.Database) ArticleCategoryDetailsRepository { + return &articleCategoryDetailsRepository{ + DB: db, + } +} + +// implement interface of IArticleCategoryDetailsRepository +func (_i *articleCategoryDetailsRepository) GetAll(req request.ArticleCategoryDetailsQueryRequest) (articleCategoryDetailss []*article_category_details.ArticleCategoryDetails, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&article_category_details.ArticleCategoryDetails{}) + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articleCategoryDetailss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *articleCategoryDetailsRepository) FindOne(id uint) (articleCategoryDetails *article_category_details.ArticleCategoryDetails, err error) { + if err := _i.DB.DB.First(&articleCategoryDetails, id).Error; err != nil { + return nil, err + } + + return articleCategoryDetails, nil +} + +func (_i *articleCategoryDetailsRepository) FindByArticleId(articleId uint) (articleCategoryDetailss []*article_category_details.ArticleCategoryDetails, err error) { + if err := _i.DB.DB.Where("article_id = ?", articleId).Preload("Category").Find(&articleCategoryDetailss).Error; err != nil { + return nil, err + } + + return articleCategoryDetailss, nil +} + +func (_i *articleCategoryDetailsRepository) Create(articleCategoryDetails *article_category_details.ArticleCategoryDetails) (err error) { + return _i.DB.DB.Create(articleCategoryDetails).Error +} + +func (_i *articleCategoryDetailsRepository) Update(id uint, articleCategoryDetails *article_category_details.ArticleCategoryDetails) (err error) { + return _i.DB.DB.Model(&article_category_details.ArticleCategoryDetails{}). + Where(&article_category_details.ArticleCategoryDetails{ID: id}). + Updates(articleCategoryDetails).Error +} + +func (_i *articleCategoryDetailsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&article_category_details.ArticleCategoryDetails{}, id).Error +} diff --git a/app/module/article_category_details/request/article_category_details.request.go b/app/module/article_category_details/request/article_category_details.request.go new file mode 100644 index 0000000..5959351 --- /dev/null +++ b/app/module/article_category_details/request/article_category_details.request.go @@ -0,0 +1,52 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity/article_category_details" + "narasi-ahli-be/utils/paginator" + "time" +) + +type ArticleCategoryDetailsGeneric interface { + ToEntity() +} + +type ArticleCategoryDetailsQueryRequest struct { + ArticleId int `json:"article_id" validate:"required"` + CategoryId int `json:"category_id" validate:"required"` + IsActive bool `json:"is_active" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ArticleCategoryDetailsCreateRequest struct { + ArticleId uint `json:"article_id" validate:"required"` + CategoryId int `json:"category_id" validate:"required"` + IsActive bool `json:"is_active" validate:"required"` +} + +func (req ArticleCategoryDetailsCreateRequest) ToEntity() *article_category_details.ArticleCategoryDetails { + return &article_category_details.ArticleCategoryDetails{ + ArticleId: req.ArticleId, + CategoryId: req.CategoryId, + IsActive: req.IsActive, + } +} + +type ArticleCategoryDetailsUpdateRequest struct { + ID uint `json:"id" validate:"required"` + ArticleId uint `json:"article_id" validate:"required"` + CategoryId int `json:"category_id" validate:"required"` + IsActive bool `json:"is_active" validate:"required"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (req ArticleCategoryDetailsUpdateRequest) ToEntity() *article_category_details.ArticleCategoryDetails { + return &article_category_details.ArticleCategoryDetails{ + ID: req.ID, + ArticleId: req.ArticleId, + CategoryId: req.CategoryId, + IsActive: req.IsActive, + CreatedAt: req.CreatedAt, + UpdatedAt: req.UpdatedAt, + } +} diff --git a/app/module/article_category_details/response/article_category_details.response.go b/app/module/article_category_details/response/article_category_details.response.go new file mode 100644 index 0000000..d2e1bf7 --- /dev/null +++ b/app/module/article_category_details/response/article_category_details.response.go @@ -0,0 +1,12 @@ +package response + +import "time" + +type ArticleCategoryDetailsResponse struct { + ID uint `json:"id"` + ArticleId uint `json:"article_id"` + CategoryId int `json:"category_id"` + IsActive bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/article_category_details/service/article_category_details.service.go b/app/module/article_category_details/service/article_category_details.service.go new file mode 100644 index 0000000..2af7f78 --- /dev/null +++ b/app/module/article_category_details/service/article_category_details.service.go @@ -0,0 +1,73 @@ +package service + +import ( + "narasi-ahli-be/app/module/article_category_details/mapper" + "narasi-ahli-be/app/module/article_category_details/repository" + "narasi-ahli-be/app/module/article_category_details/request" + "narasi-ahli-be/app/module/article_category_details/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// ArticleCategoryDetailsService +type articleCategoryDetailsService struct { + Repo repository.ArticleCategoryDetailsRepository + Log zerolog.Logger +} + +// ArticleCategoryDetailsService define interface of IArticleCategoryDetailsService +type ArticleCategoryDetailsService interface { + All(req request.ArticleCategoryDetailsQueryRequest) (articleCategoryDetails []*response.ArticleCategoryDetailsResponse, paging paginator.Pagination, err error) + Show(id uint) (articleCategoryDetails *response.ArticleCategoryDetailsResponse, err error) + Save(req request.ArticleCategoryDetailsCreateRequest) (err error) + Update(id uint, req request.ArticleCategoryDetailsUpdateRequest) (err error) + Delete(id uint) error +} + +// NewArticleCategoryDetailsService init ArticleCategoryDetailsService +func NewArticleCategoryDetailsService(repo repository.ArticleCategoryDetailsRepository, log zerolog.Logger) ArticleCategoryDetailsService { + + return &articleCategoryDetailsService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of ArticleCategoryDetailsService +func (_i *articleCategoryDetailsService) All(req request.ArticleCategoryDetailsQueryRequest) (articleCategoryDetailss []*response.ArticleCategoryDetailsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + articleCategoryDetailss = append(articleCategoryDetailss, mapper.ArticleCategoryDetailsResponseMapper(result)) + } + + return +} + +func (_i *articleCategoryDetailsService) Show(id uint) (articleCategoryDetails *response.ArticleCategoryDetailsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.ArticleCategoryDetailsResponseMapper(result), nil +} + +func (_i *articleCategoryDetailsService) Save(req request.ArticleCategoryDetailsCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *articleCategoryDetailsService) Update(id uint, req request.ArticleCategoryDetailsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *articleCategoryDetailsService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/article_comments/article_comments.module.go b/app/module/article_comments/article_comments.module.go new file mode 100644 index 0000000..252a04f --- /dev/null +++ b/app/module/article_comments/article_comments.module.go @@ -0,0 +1,54 @@ +package article_comments + +import ( + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" + "narasi-ahli-be/app/module/article_comments/controller" + "narasi-ahli-be/app/module/article_comments/repository" + "narasi-ahli-be/app/module/article_comments/service" +) + +// struct of ArticleCommentsRouter +type ArticleCommentsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ArticleComments module +var NewArticleCommentsModule = fx.Options( + // register repository of ArticleComments module + fx.Provide(repository.NewArticleCommentsRepository), + + // register service of ArticleComments module + fx.Provide(service.NewArticleCommentsService), + + // register controller of ArticleComments module + fx.Provide(controller.NewController), + + // register router of ArticleComments module + fx.Provide(NewArticleCommentsRouter), +) + +// init ArticleCommentsRouter +func NewArticleCommentsRouter(fiber *fiber.App, controller *controller.Controller) *ArticleCommentsRouter { + return &ArticleCommentsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ArticleComments module +func (_i *ArticleCommentsRouter) RegisterArticleCommentsRoutes() { + // define controllers + articleCommentsController := _i.Controller.ArticleComments + + // define routes + _i.App.Route("/article-comments", func(router fiber.Router) { + router.Get("/", articleCommentsController.All) + router.Get("/:id", articleCommentsController.Show) + router.Post("/", articleCommentsController.Save) + router.Put("/:id", articleCommentsController.Update) + router.Delete("/:id", articleCommentsController.Delete) + router.Post("/approval", articleCommentsController.Approval) + }) +} diff --git a/app/module/article_comments/controller/article_comments.controller.go b/app/module/article_comments/controller/article_comments.controller.go new file mode 100644 index 0000000..518f63a --- /dev/null +++ b/app/module/article_comments/controller/article_comments.controller.go @@ -0,0 +1,240 @@ +package controller + +import ( + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" + "narasi-ahli-be/app/module/article_comments/request" + "narasi-ahli-be/app/module/article_comments/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" +) + +type articleCommentsController struct { + articleCommentsService service.ArticleCommentsService + Log zerolog.Logger +} + +type ArticleCommentsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + Approval(c *fiber.Ctx) error +} + +func NewArticleCommentsController(articleCommentsService service.ArticleCommentsService, log zerolog.Logger) ArticleCommentsController { + return &articleCommentsController{ + articleCommentsService: articleCommentsService, + Log: log, + } +} + +// All get all ArticleComments +// @Summary Get all ArticleComments +// @Description API for getting all ArticleComments +// @Tags ArticleComments +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.ArticleCommentsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-comments [get] +func (_i *articleCommentsController) All(c *fiber.Ctx) error { + // Get from context + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.ArticleCommentsQueryRequestContext{ + Message: c.Query("message"), + ArticleId: c.Query("articleId"), + CommentFrom: c.Query("commentFrom"), + ParentId: c.Query("parentId"), + IsPublic: c.Query("isPublic"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + articleCommentsData, paging, err := _i.articleCommentsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleComments list successfully retrieved"}, + Data: articleCommentsData, + Meta: paging, + }) +} + +// Show get one ArticleComments +// @Summary Get one ArticleComments +// @Description API for getting one ArticleComments +// @Tags ArticleComments +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "ArticleComments ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-comments/{id} [get] +func (_i *articleCommentsController) Show(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + articleCommentsData, err := _i.articleCommentsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleComments successfully retrieved"}, + Data: articleCommentsData, + }) +} + +// Save create ArticleComments +// @Summary Create ArticleComments +// @Description API for create ArticleComments +// @Tags ArticleComments +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ArticleCommentsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-comments [post] +func (_i *articleCommentsController) Save(c *fiber.Ctx) error { + // Get from context + req := new(request.ArticleCommentsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + dataResult, err := _i.articleCommentsService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleComments successfully created"}, + Data: dataResult, + }) +} + +// Update update ArticleComments +// @Summary update ArticleComments +// @Description API for update ArticleComments +// @Tags ArticleComments +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ArticleCommentsUpdateRequest true "Required payload" +// @Param id path int true "ArticleComments ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-comments/{id} [put] +func (_i *articleCommentsController) Update(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ArticleCommentsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.articleCommentsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleComments successfully updated"}, + }) +} + +// Delete delete ArticleComments +// @Summary delete ArticleComments +// @Description API for delete ArticleComments +// @Tags ArticleComments +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "ArticleComments ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-comments/{id} [delete] +func (_i *articleCommentsController) Delete(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.articleCommentsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleComments successfully deleted"}, + }) +} + +// Approval ArticleComments +// @Summary Approval ArticleComments +// @Description API for Approval ArticleComments +// @Tags ArticleComments +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ArticleCommentsApprovalRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-comments/approval [post] +func (_i *articleCommentsController) Approval(c *fiber.Ctx) error { + // Get from context + req := new(request.ArticleCommentsApprovalRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.articleCommentsService.Approval(req.ID, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleComments successfully reviewed"}, + }) +} diff --git a/app/module/article_comments/controller/controller.go b/app/module/article_comments/controller/controller.go new file mode 100644 index 0000000..ef25b9e --- /dev/null +++ b/app/module/article_comments/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/article_comments/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + ArticleComments ArticleCommentsController +} + +func NewController(ArticleCommentsService service.ArticleCommentsService, log zerolog.Logger) *Controller { + return &Controller{ + ArticleComments: NewArticleCommentsController(ArticleCommentsService, log), + } +} diff --git a/app/module/article_comments/mapper/article_comments.mapper.go b/app/module/article_comments/mapper/article_comments.mapper.go new file mode 100644 index 0000000..634e366 --- /dev/null +++ b/app/module/article_comments/mapper/article_comments.mapper.go @@ -0,0 +1,36 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/article_comments/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + + ) + +func ArticleCommentsResponseMapper(articleCommentsReq *entity.ArticleComments, usersRepo usersRepository.UsersRepository) (articleCommentsRes *res.ArticleCommentsResponse) { + if articleCommentsReq != nil { + + commentFromName := "" + if articleCommentsReq.CommentFrom != nil { + findUser, _ := usersRepo.FindOne(*articleCommentsReq.CommentFrom) + if findUser != nil { + commentFromName = findUser.Fullname + } + } + + articleCommentsRes = &res.ArticleCommentsResponse{ + ID: articleCommentsReq.ID, + Message: articleCommentsReq.Message, + ArticleId: articleCommentsReq.ArticleId, + CommentFromId: articleCommentsReq.CommentFrom, + CommentFromName: &commentFromName, + ParentId: articleCommentsReq.ParentId, + IsPublic: articleCommentsReq.IsPublic, + StatusId: articleCommentsReq.StatusId, + IsActive: articleCommentsReq.IsActive, + CreatedAt: articleCommentsReq.CreatedAt, + UpdatedAt: articleCommentsReq.UpdatedAt, + } + } + return articleCommentsRes +} diff --git a/app/module/article_comments/repository/article_comments.repository.go b/app/module/article_comments/repository/article_comments.repository.go new file mode 100644 index 0000000..2109ac0 --- /dev/null +++ b/app/module/article_comments/repository/article_comments.repository.go @@ -0,0 +1,108 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_comments/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + + "github.com/rs/zerolog" +) + +type articleCommentsRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// ArticleCommentsRepository define interface of IArticleCommentsRepository +type ArticleCommentsRepository interface { + GetAll(req request.ArticleCommentsQueryRequest) (articleCommentss []*entity.ArticleComments, paging paginator.Pagination, err error) + FindOne(id uint) (articleComments *entity.ArticleComments, err error) + Create(articleComments *entity.ArticleComments) (articleCommentsReturn *entity.ArticleComments, err error) + Update(id uint, articleComments *entity.ArticleComments) (err error) + Delete(id uint) (err error) +} + +func NewArticleCommentsRepository(db *database.Database, logger zerolog.Logger) ArticleCommentsRepository { + return &articleCommentsRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of IArticleCommentsRepository +func (_i *articleCommentsRepository) GetAll(req request.ArticleCommentsQueryRequest) (articleCommentss []*entity.ArticleComments, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.ArticleComments{}) + query = query.Where("is_active = ?", true) + + if req.Message != nil && *req.Message != "" { + message := strings.ToLower(*req.Message) + query = query.Where("LOWER(message) LIKE ?", "%"+strings.ToLower(message)+"%") + } + if req.ArticleId != nil { + query = query.Where("article_id = ?", req.ArticleId) + } + if req.CommentFrom != nil { + query = query.Where("comment_from = ?", req.CommentFrom) + } + if req.ParentId != nil { + query = query.Where("parent_id = ?", req.ParentId) + } + if req.IsPublic != nil { + query = query.Where("is_public = ?", req.IsPublic) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articleCommentss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *articleCommentsRepository) FindOne(id uint) (articleComments *entity.ArticleComments, err error) { + query := _i.DB.DB + if err := query.First(&articleComments, id).Error; err != nil { + return nil, err + } + + return articleComments, nil +} + +func (_i *articleCommentsRepository) Create(articleComments *entity.ArticleComments) (articleCommentsReturn *entity.ArticleComments, err error) { + result := _i.DB.DB.Create(articleComments) + return articleComments, result.Error +} + +func (_i *articleCommentsRepository) Update(id uint, articleComments *entity.ArticleComments) (err error) { + articleCommentsMap, err := utilSvc.StructToMap(articleComments) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.ArticleComments{}). + Where(&entity.ArticleComments{ID: id}). + Updates(articleCommentsMap).Error +} + +func (_i *articleCommentsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.ArticleComments{}, id).Error +} diff --git a/app/module/article_comments/request/article_comments.request.go b/app/module/article_comments/request/article_comments.request.go new file mode 100644 index 0000000..ef678fd --- /dev/null +++ b/app/module/article_comments/request/article_comments.request.go @@ -0,0 +1,111 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type ArticleCommentsGeneric interface { + ToEntity() +} + +type ArticleCommentsQueryRequest struct { + Message *string `json:"message"` + ArticleId *int `json:"articleId"` + CommentFrom *int `json:"commentFrom"` + ParentId *int `json:"parentId"` + IsPublic *bool `json:"isPublic"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ArticleCommentsCreateRequest struct { + Message string `json:"message" validate:"required"` + ArticleId uint `json:"articleId" validate:"required"` + ParentId *int `json:"parentId"` + IsPublic *bool `json:"isPublic"` +} + +func (req ArticleCommentsCreateRequest) ToEntity() *entity.ArticleComments { + return &entity.ArticleComments{ + Message: req.Message, + ArticleId: req.ArticleId, + ParentId: req.ParentId, + StatusId: 0, + } +} + +type ArticleCommentsUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Message string `json:"message" validate:"required"` + ArticleId uint `json:"articleId" validate:"required"` + ParentId *int `json:"parentId"` + IsPublic *bool `json:"isPublic"` +} + +func (req ArticleCommentsUpdateRequest) ToEntity() *entity.ArticleComments { + return &entity.ArticleComments{ + ID: req.ID, + Message: req.Message, + ArticleId: req.ArticleId, + ParentId: req.ParentId, + UpdatedAt: time.Now(), + } +} + +type ArticleCommentsApprovalRequest struct { + ID uint `json:"id" validate:"required"` + StatusId int `json:"statusId" validate:"required"` +} + +func (req ArticleCommentsApprovalRequest) ToEntity() *entity.ArticleComments { + approvedNow := time.Now() + return &entity.ArticleComments{ + ID: req.ID, + StatusId: req.StatusId, + ApprovedAt: &approvedNow, + } +} + +type ArticleCommentsQueryRequestContext struct { + Message string `json:"message"` + ArticleId string `json:"articleId"` + CommentFrom string `json:"commentFrom"` + ParentId string `json:"parentId"` + IsPublic string `json:"isPublic"` +} + +func (req ArticleCommentsQueryRequestContext) ToParamRequest() ArticleCommentsQueryRequest { + var request ArticleCommentsQueryRequest + + if message := req.Message; message != "" { + request.Message = &message + } + if articleIdStr := req.ArticleId; articleIdStr != "" { + articleId, err := strconv.Atoi(articleIdStr) + if err == nil { + request.ArticleId = &articleId + } + } + if commentFromStr := req.CommentFrom; commentFromStr != "" { + commentFrom, err := strconv.Atoi(commentFromStr) + if err == nil { + request.CommentFrom = &commentFrom + } + } + if parentIdStr := req.ParentId; parentIdStr != "" { + parentId, err := strconv.Atoi(parentIdStr) + if err == nil { + request.ParentId = &parentId + } + } + if isPublicStr := req.IsPublic; isPublicStr != "" { + isPublic, err := strconv.ParseBool(isPublicStr) + if err == nil { + request.IsPublic = &isPublic + } + } + + return request +} diff --git a/app/module/article_comments/response/article_comments.response.go b/app/module/article_comments/response/article_comments.response.go new file mode 100644 index 0000000..ff86554 --- /dev/null +++ b/app/module/article_comments/response/article_comments.response.go @@ -0,0 +1,17 @@ +package response + +import "time" + +type ArticleCommentsResponse struct { + ID uint `json:"id"` + Message string `json:"message"` + ArticleId uint `json:"articleId"` + CommentFromId *uint `json:"commentFromId"` + CommentFromName *string `json:"commentFromName"` + ParentId *int `json:"parentId"` + StatusId int `json:"statusId"` + IsPublic bool `json:"isPublic"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/article_comments/service/article_comments.service.go b/app/module/article_comments/service/article_comments.service.go new file mode 100644 index 0000000..97d8737 --- /dev/null +++ b/app/module/article_comments/service/article_comments.service.go @@ -0,0 +1,105 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_comments/mapper" + "narasi-ahli-be/app/module/article_comments/repository" + "narasi-ahli-be/app/module/article_comments/request" + "narasi-ahli-be/app/module/article_comments/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" + + utilSvc "narasi-ahli-be/utils/service" +) + +// ArticleCommentsService +type articleCommentsService struct { + Repo repository.ArticleCommentsRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +// ArticleCommentsService define interface of IArticleCommentsService +type ArticleCommentsService interface { + All(req request.ArticleCommentsQueryRequest) (articleComments []*response.ArticleCommentsResponse, paging paginator.Pagination, err error) + Show(id uint) (articleComments *response.ArticleCommentsResponse, err error) + Save(req request.ArticleCommentsCreateRequest, authToken string) (articleComments *entity.ArticleComments, err error) + Update(id uint, req request.ArticleCommentsUpdateRequest) (err error) + Delete(id uint) error + Approval(id uint, req request.ArticleCommentsApprovalRequest) (err error) +} + +// NewArticleCommentsService init ArticleCommentsService +func NewArticleCommentsService(repo repository.ArticleCommentsRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) ArticleCommentsService { + + return &articleCommentsService{ + Repo: repo, + Log: log, + UsersRepo: usersRepo, + } +} + +// All implement interface of ArticleCommentsService +func (_i *articleCommentsService) All(req request.ArticleCommentsQueryRequest) (articleCommentss []*response.ArticleCommentsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + articleCommentss = append(articleCommentss, mapper.ArticleCommentsResponseMapper(result, _i.UsersRepo)) + } + + return +} + +func (_i *articleCommentsService) Show(id uint) (articleComments *response.ArticleCommentsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.ArticleCommentsResponseMapper(result, _i.UsersRepo), nil +} + +func (_i *articleCommentsService) Save(req request.ArticleCommentsCreateRequest, authToken string) (articleComments *entity.ArticleComments, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + newReq := req.ToEntity() + + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + newReq.CommentFrom = &createdBy.ID + newReq.IsActive = true + + return _i.Repo.Create(newReq) +} + +func (_i *articleCommentsService) Update(id uint, req request.ArticleCommentsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *articleCommentsService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + result.IsActive = false + return _i.Repo.Update(id, result) +} + +func (_i *articleCommentsService) Approval(id uint, req request.ArticleCommentsApprovalRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + if newReq.StatusId == 1 || newReq.StatusId == 2 { + newReq.IsPublic = true + } else { + newReq.IsPublic = false + } + + return _i.Repo.Update(id, newReq) +} diff --git a/app/module/article_files/article_files.module.go b/app/module/article_files/article_files.module.go new file mode 100644 index 0000000..3456fa1 --- /dev/null +++ b/app/module/article_files/article_files.module.go @@ -0,0 +1,58 @@ +package article_files + +import ( + "narasi-ahli-be/app/module/article_files/controller" + "narasi-ahli-be/app/module/article_files/repository" + "narasi-ahli-be/app/module/article_files/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ArticleFilesRouter +type ArticleFilesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ArticleFiles module +var NewArticleFilesModule = fx.Options( + // register repository of ArticleFiles module + fx.Provide(repository.NewArticleFilesRepository), + + // register service of ArticleFiles module + fx.Provide(service.NewArticleFilesService), + fx.Provide(service.NewUploadService), + fx.Provide(service.NewUploadManager), + + // register controller of ArticleFiles module + fx.Provide(controller.NewController), + + // register router of ArticleFiles module + fx.Provide(NewArticleFilesRouter), +) + +// init ArticleFilesRouter +func NewArticleFilesRouter(fiber *fiber.App, controller *controller.Controller) *ArticleFilesRouter { + return &ArticleFilesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ArticleFiles module +func (_i *ArticleFilesRouter) RegisterArticleFilesRoutes() { + // define controllers + articleFilesController := _i.Controller.ArticleFiles + + // define routes + _i.App.Route("/article-files", func(router fiber.Router) { + router.Get("/", articleFilesController.All) + router.Get("/:id", articleFilesController.Show) + router.Post("/:articleId", articleFilesController.Save) + router.Put("/:id", articleFilesController.Update) + router.Delete("/:id", articleFilesController.Delete) + router.Get("/viewer/:filename", articleFilesController.Viewer) + router.Get("/upload-status/:uploadId", articleFilesController.GetUploadStatus) + }) +} diff --git a/app/module/article_files/controller/article_files.controller.go b/app/module/article_files/controller/article_files.controller.go new file mode 100644 index 0000000..ed9c071 --- /dev/null +++ b/app/module/article_files/controller/article_files.controller.go @@ -0,0 +1,246 @@ +package controller + +import ( + "fmt" + "narasi-ahli-be/app/module/article_files/request" + "narasi-ahli-be/app/module/article_files/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type articleFilesController struct { + articleFilesService service.ArticleFilesService +} + +type ArticleFilesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error + GetUploadStatus(c *fiber.Ctx) error +} + +func NewArticleFilesController(articleFilesService service.ArticleFilesService) ArticleFilesController { + return &articleFilesController{ + articleFilesService: articleFilesService, + } +} + +// All ArticleFiles +// @Summary Get all ArticleFiles +// @Description API for getting all ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.ArticleFilesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files [get] +func (_i *articleFilesController) All(c *fiber.Ctx) error { + // Get from context + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.ArticleFilesQueryRequestContext{ + ArticleId: c.Query("articleId"), + FileName: c.Query("fileName"), + StatusId: c.Query("statusId"), + IsPublish: c.Query("isPublish"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + articleFilesData, paging, err := _i.articleFilesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleFiles list successfully retrieved"}, + Data: articleFilesData, + Meta: paging, + }) +} + +// Show ArticleFiles +// @Summary Get one ArticleFiles +// @Description API for getting one ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "ArticleFiles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files/{id} [get] +func (_i *articleFilesController) Show(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + articleFilesData, err := _i.articleFilesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleFiles successfully retrieved"}, + Data: articleFilesData, + }) +} + +// Save ArticleFiles +// @Summary Upload ArticleFiles +// @Description API for create ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Produce json +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param files formData file true "Upload file" multiple true +// @Param articleId path int true "Article ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files/{articleId} [post] +func (_i *articleFilesController) Save(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("articleId"), 10, 0) + if err != nil { + return err + } + + err = _i.articleFilesService.Save(c, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleFiles successfully upload"}, + }) +} + +// Update ArticleFiles +// @Summary Update ArticleFiles +// @Description API for update ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ArticleFilesUpdateRequest true "Required payload" +// @Param id path int true "ArticleFiles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files/{id} [put] +func (_i *articleFilesController) Update(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ArticleFilesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.articleFilesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleFiles successfully updated"}, + }) +} + +// Delete ArticleFiles +// @Summary Delete ArticleFiles +// @Description API for delete ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "ArticleFiles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files/{id} [delete] +func (_i *articleFilesController) Delete(c *fiber.Ctx) error { + // Get from context + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.articleFilesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleFiles successfully deleted"}, + }) +} + +// Viewer ArticleFiles +// @Summary Viewer ArticleFiles +// @Description API for Viewer ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param filename path string true "Article File Name" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files/viewer/{filename} [get] +func (_i *articleFilesController) Viewer(c *fiber.Ctx) error { + // Get from context + return _i.articleFilesService.Viewer(c) +} + +// GetUploadStatus ArticleFiles +// @Summary GetUploadStatus ArticleFiles +// @Description API for GetUploadStatus ArticleFiles +// @Tags Article Files +// @Security Bearer +// @Param uploadId path string true "Upload ID of ArticleFiles" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /article-files/upload-status/{uploadId} [get] +func (_i *articleFilesController) GetUploadStatus(c *fiber.Ctx) error { + progress, _ := _i.articleFilesService.GetUploadStatus(c) + progressMessage := fmt.Sprintf("Upload Progress: %d%%", progress) + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Upload Status Retrieve"}, + Data: progressMessage, + }) +} diff --git a/app/module/article_files/controller/controller.go b/app/module/article_files/controller/controller.go new file mode 100644 index 0000000..a009ee1 --- /dev/null +++ b/app/module/article_files/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/article_files/service" + +type Controller struct { + ArticleFiles ArticleFilesController +} + +func NewController(ArticleFilesService service.ArticleFilesService) *Controller { + return &Controller{ + ArticleFiles: NewArticleFilesController(ArticleFilesService), + } +} diff --git a/app/module/article_files/mapper/article_files.mapper.go b/app/module/article_files/mapper/article_files.mapper.go new file mode 100644 index 0000000..b0ae717 --- /dev/null +++ b/app/module/article_files/mapper/article_files.mapper.go @@ -0,0 +1,37 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/article_files/response" +) + +func ArticleFilesResponseMapper(articleFilesReq *entity.ArticleFiles, host string) (articleFilesRes *res.ArticleFilesResponse) { + fileUrl := host + "/article-files/viewer/" + if articleFilesReq.FileName != nil { + fileUrl += *articleFilesReq.FileName + } + + if articleFilesReq != nil { + articleFilesRes = &res.ArticleFilesResponse{ + ID: articleFilesReq.ID, + ArticleId: articleFilesReq.ArticleId, + FilePath: articleFilesReq.FilePath, + FileUrl: &fileUrl, + FileName: articleFilesReq.FileName, + FileThumbnail: articleFilesReq.FileThumbnail, + FileAlt: articleFilesReq.FileAlt, + WidthPixel: articleFilesReq.WidthPixel, + HeightPixel: articleFilesReq.HeightPixel, + Size: articleFilesReq.Size, + DownloadCount: articleFilesReq.DownloadCount, + CreatedById: articleFilesReq.CreatedById, + StatusId: articleFilesReq.StatusId, + IsPublish: articleFilesReq.IsPublish, + PublishedAt: articleFilesReq.PublishedAt, + IsActive: articleFilesReq.IsActive, + CreatedAt: articleFilesReq.CreatedAt, + UpdatedAt: articleFilesReq.UpdatedAt, + } + } + return articleFilesRes +} diff --git a/app/module/article_files/repository/article_files.repository.go b/app/module/article_files/repository/article_files.repository.go new file mode 100644 index 0000000..7dfe804 --- /dev/null +++ b/app/module/article_files/repository/article_files.repository.go @@ -0,0 +1,123 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/article_files/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" +) + +type articleFilesRepository struct { + DB *database.Database +} + +// ArticleFilesRepository define interface of IArticleFilesRepository +type ArticleFilesRepository interface { + GetAll(req request.ArticleFilesQueryRequest) (articleFiless []*entity.ArticleFiles, paging paginator.Pagination, err error) + FindOne(id uint) (articleFiles *entity.ArticleFiles, err error) + FindByArticle(articleId uint) (articleFiles []*entity.ArticleFiles, err error) + FindByFilename(filename string) (articleFiles *entity.ArticleFiles, err error) + Create(articleFiles *entity.ArticleFiles) (err error) + Update(id uint, articleFiles *entity.ArticleFiles) (err error) + Delete(id uint) (err error) +} + +func NewArticleFilesRepository(db *database.Database) ArticleFilesRepository { + return &articleFilesRepository{ + DB: db, + } +} + +// implement interface of IArticleFilesRepository +func (_i *articleFilesRepository) GetAll(req request.ArticleFilesQueryRequest) (articleFiless []*entity.ArticleFiles, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.ArticleFiles{}) + query = query.Where("is_active = ?", true) + + if req.ArticleId != nil { + query = query.Where("article_id = ?", req.ArticleId) + } + if req.FileName != nil && *req.FileName != "" { + fileName := strings.ToLower(*req.FileName) + query = query.Where("LOWER(file_name) LIKE ?", "%"+strings.ToLower(fileName)+"%") + } + if req.IsPublish != nil { + query = query.Where("is_publish = ?", req.IsPublish) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articleFiless).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *articleFilesRepository) FindOne(id uint) (articleFiles *entity.ArticleFiles, err error) { + query := _i.DB.DB + + if err := query.First(&articleFiles, id).Error; err != nil { + return nil, err + } + + return articleFiles, nil +} + +func (_i *articleFilesRepository) FindByArticle(articleId uint) (articleFiles []*entity.ArticleFiles, err error) { + query := _i.DB.DB.Where("article_id = ?", articleId) + + if err := query.Find(&articleFiles).Error; err != nil { + return nil, err + } + + return articleFiles, nil +} + +func (_i *articleFilesRepository) FindByFilename(articleFilename string) (articleFiles *entity.ArticleFiles, err error) { + query := _i.DB.DB.Where("file_name = ?", articleFilename) + + if err := query.First(&articleFiles).Error; err != nil { + return nil, err + } + + return articleFiles, nil +} + +func (_i *articleFilesRepository) Create(articleFiles *entity.ArticleFiles) (err error) { + return _i.DB.DB.Create(articleFiles).Error +} + +func (_i *articleFilesRepository) Update(id uint, articleFiles *entity.ArticleFiles) (err error) { + articleFilesMap, err := utilSvc.StructToMap(articleFiles) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.ArticleFiles{}). + Where(&entity.ArticleFiles{ID: id}). + Updates(articleFilesMap).Error +} + +func (_i *articleFilesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.ArticleFiles{}, id).Error +} diff --git a/app/module/article_files/request/article_files.request.go b/app/module/article_files/request/article_files.request.go new file mode 100644 index 0000000..90416c9 --- /dev/null +++ b/app/module/article_files/request/article_files.request.go @@ -0,0 +1,120 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type ArticleFilesGeneric interface { + ToEntity() +} + +type ArticleFilesQueryRequest struct { + ArticleId *int `json:"articleId"` + FileName *string `json:"fileName"` + StatusId *int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ArticleFilesCreateRequest struct { + ArticleId uint `json:"articleId" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + UploadId *string `json:"uploadId"` + FilePath *string `json:"filePath"` + FileUrl *string `json:"fileUrl"` + FileName *string `json:"fileName"` + FileThumbnail *string `json:"fileThumbnail"` + FileAlt *string `json:"fileAlt"` + WidthPixel *string `json:"widthPixel"` + HeightPixel *string `json:"heightPixel"` + Size *string `json:"size"` +} + +func (req ArticleFilesCreateRequest) ToEntity() *entity.ArticleFiles { + return &entity.ArticleFiles{ + ArticleId: req.ArticleId, + UploadID: req.UploadId, + FilePath: req.FilePath, + FileUrl: req.FileUrl, + FileName: req.FileName, + FileThumbnail: req.FileThumbnail, + FileAlt: req.FileAlt, + WidthPixel: req.WidthPixel, + HeightPixel: req.HeightPixel, + Size: req.Size, + StatusId: req.StatusId, + } +} + +type ArticleFilesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + ArticleId uint `json:"articleId" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + FilePath *string `json:"filePath"` + FileUrl *string `json:"fileUrl"` + FileName *string `json:"fileName"` + FileThumbnail *string `json:"fileThumbnail"` + FileAlt *string `json:"fileAlt"` + WidthPixel *string `json:"widthPixel"` + HeightPixel *string `json:"heightPixel"` + Size *string `json:"size"` + IsPublish *bool `json:"isPublish" validate:"required"` + PublishedAt *time.Time `json:"publishedAt" validate:"required"` +} + +func (req ArticleFilesUpdateRequest) ToEntity() *entity.ArticleFiles { + return &entity.ArticleFiles{ + ID: req.ID, + ArticleId: req.ArticleId, + FilePath: req.FilePath, + FileUrl: req.FileUrl, + FileName: req.FileName, + FileThumbnail: req.FileThumbnail, + FileAlt: req.FileAlt, + WidthPixel: req.WidthPixel, + HeightPixel: req.HeightPixel, + Size: req.Size, + StatusId: req.StatusId, + IsPublish: req.IsPublish, + PublishedAt: req.PublishedAt, + UpdatedAt: time.Now(), + } +} + +type ArticleFilesQueryRequestContext struct { + ArticleId string `json:"articleId"` + FileName string `json:"fileName"` + StatusId string `json:"statusId"` + IsPublish string `json:"isPublish"` +} + +func (req ArticleFilesQueryRequestContext) ToParamRequest() ArticleFilesQueryRequest { + var request ArticleFilesQueryRequest + + if articleIdStr := req.ArticleId; articleIdStr != "" { + articleId, err := strconv.Atoi(articleIdStr) + if err == nil { + request.ArticleId = &articleId + } + } + if fileName := req.FileName; fileName != "" { + request.FileName = &fileName + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + if isPublishStr := req.IsPublish; isPublishStr != "" { + isPublish, err := strconv.ParseBool(isPublishStr) + if err == nil { + request.IsPublish = &isPublish + } + } + + return request +} diff --git a/app/module/article_files/response/article_files.response.go b/app/module/article_files/response/article_files.response.go new file mode 100644 index 0000000..465d9d2 --- /dev/null +++ b/app/module/article_files/response/article_files.response.go @@ -0,0 +1,24 @@ +package response + +import "time" + +type ArticleFilesResponse struct { + ID uint `json:"id"` + ArticleId uint `json:"article_id"` + FilePath *string `json:"file_path"` + FileUrl *string `json:"file_url"` + FileName *string `json:"file_name"` + FileThumbnail *string `json:"file_thumbnail"` + FileAlt *string `json:"file_alt"` + WidthPixel *string `json:"width_pixel"` + HeightPixel *string `json:"height_pixel"` + Size *string `json:"size"` + DownloadCount *int `json:"download_count"` + CreatedById int `json:"created_by_id"` + StatusId int `json:"status_id"` + IsPublish *bool `json:"is_publish"` + PublishedAt *time.Time `json:"published_at"` + IsActive bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/article_files/service/article_files.service.go b/app/module/article_files/service/article_files.service.go new file mode 100644 index 0000000..97d66ce --- /dev/null +++ b/app/module/article_files/service/article_files.service.go @@ -0,0 +1,446 @@ +package service + +import ( + "context" + "fmt" + "io" + "log" + "math/rand" + "mime" + "mime/multipart" + "narasi-ahli-be/app/module/article_files/mapper" + "narasi-ahli-be/app/module/article_files/repository" + "narasi-ahli-be/app/module/article_files/request" + "narasi-ahli-be/app/module/article_files/response" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +// ArticleFilesService +type articleFilesService struct { + Repo repository.ArticleFilesRepository + Log zerolog.Logger + Cfg *config.Config + MinioStorage *minioStorage.MinioStorage +} + +// ArticleFilesService define interface of IArticleFilesService +type ArticleFilesService interface { + All(req request.ArticleFilesQueryRequest) (articleFiles []*response.ArticleFilesResponse, paging paginator.Pagination, err error) + Show(id uint) (articleFiles *response.ArticleFilesResponse, err error) + Save(c *fiber.Ctx, id uint) error + SaveAsync(c *fiber.Ctx, id uint) error + Update(id uint, req request.ArticleFilesUpdateRequest) (err error) + GetUploadStatus(c *fiber.Ctx) (progress int, err error) + Delete(id uint) error + Viewer(c *fiber.Ctx) error +} + +// NewArticleFilesService init ArticleFilesService +func NewArticleFilesService(repo repository.ArticleFilesRepository, log zerolog.Logger, cfg *config.Config, minioStorage *minioStorage.MinioStorage) ArticleFilesService { + + return &articleFilesService{ + Repo: repo, + Log: log, + Cfg: cfg, + MinioStorage: minioStorage, + } +} + +var ( + progressMap = make(map[string]int) // Menyimpan progress upload per UploadID + progressLock = sync.Mutex{} +) + +type progressWriter struct { + uploadID string + totalSize int64 + uploadedSize *int64 +} + +// All implement interface of ArticleFilesService +func (_i *articleFilesService) All(req request.ArticleFilesQueryRequest) (articleFiless []*response.ArticleFilesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + host := _i.Cfg.App.Domain + + for _, result := range results { + articleFiless = append(articleFiless, mapper.ArticleFilesResponseMapper(result, host)) + } + + return +} + +func (_i *articleFilesService) Show(id uint) (articleFiles *response.ArticleFilesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + host := _i.Cfg.App.Domain + + return mapper.ArticleFilesResponseMapper(result, host), nil +} + +func (_i *articleFilesService) SaveAsync(c *fiber.Ctx, id uint) (err error) { + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + ctx := context.Background() + + form, err := c.MultipartForm() + + if err != nil { + return err + } + //filess := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + for _, files := range form.File { + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top"). + Interface("files", files).Msg("") + + for _, fileHeader := range files { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop"). + Interface("data", fileHeader).Msg("") + + filename := filepath.Base(fileHeader.Filename) + filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(fileHeader.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + uploadID := strconv.Itoa(randUniqueId) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + + objectName := fmt.Sprintf("articles/upload/%d/%d/%s", now.Year(), now.Month(), newFilename) + fileSize := strconv.FormatInt(fileHeader.Size, 10) + fileSizeInt := fileHeader.Size + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top"). + Interface("Start upload", uploadID).Msg("") + + req := request.ArticleFilesCreateRequest{ + ArticleId: id, + UploadId: &uploadID, + FilePath: &objectName, + FileName: &newFilename, + FileAlt: &filenameAlt, + Size: &fileSize, + } + + err = _i.Repo.Create(req.ToEntity()) + if err != nil { + return err + } + + src, err := fileHeader.Open() + if err != nil { + return err + } + defer src.Close() + + tempFilePath := fmt.Sprintf("/tmp/%s", newFilename) + tempFile, err := os.Create(tempFilePath) + if err != nil { + return err + } + defer tempFile.Close() + + // Copy file ke direktori sementara + _, err = io.Copy(tempFile, src) + if err != nil { + return err + } + + go uploadToMinIO(ctx, _i.Log, minioClient, uploadID, tempFilePath, bucketName, objectName, fileSizeInt) + } + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "User:All"). + Interface("data", "Successfully uploaded").Msg("") + + return +} + +func (_i *articleFilesService) Save(c *fiber.Ctx, id uint) (err error) { + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + + if err != nil { + return err + } + //filess := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + for _, files := range form.File { + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top"). + Interface("files", files).Msg("") + + for _, fileHeader := range files { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop"). + Interface("data", fileHeader).Msg("") + + src, err := fileHeader.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(fileHeader.Filename) + filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(fileHeader.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + + objectName := fmt.Sprintf("articles/upload/%d/%d/%s", now.Year(), now.Month(), newFilename) + fileSize := strconv.FormatInt(fileHeader.Size, 10) + + req := request.ArticleFilesCreateRequest{ + ArticleId: id, + FilePath: &objectName, + FileName: &newFilename, + FileAlt: &filenameAlt, + Size: &fileSize, + } + + err = _i.Repo.Create(req.ToEntity()) + if err != nil { + return err + } + + // Upload file ke MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + } + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "User:All"). + Interface("data", "Successfully uploaded").Msg("") + + return +} + +func (_i *articleFilesService) Update(id uint, req request.ArticleFilesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *articleFilesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + result.IsActive = false + return _i.Repo.Update(id, result) +} + +func (_i *articleFilesService) Viewer(c *fiber.Ctx) (err error) { + filename := c.Params("filename") + result, err := _i.Repo.FindByFilename(filename) + if err != nil { + return err + } + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := *result.FilePath + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads"). + Interface("data", objectName).Msg("") + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + log.Fatalln(err) + } + defer fileContent.Close() + + // Tentukan Content-Type berdasarkan ekstensi file + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) + if contentType == "" { + contentType = "application/octet-stream" // fallback jika tidak ada tipe MIME yang cocok + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return +} + +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // jika tidak ada ekstensi, kembalikan string kosong + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // ambil ekstensi terakhir + return parts[len(parts)-1] +} + +func uploadTempFile(log zerolog.Logger, fileHeader *multipart.FileHeader, filePath string) { + src, err := fileHeader.Open() + if err != nil { + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-0"). + Interface("err", err).Msg("") + } + defer src.Close() + + tempFile, err := os.Create(filePath) + if err != nil { + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-1"). + Interface("err", err).Msg("") + } + defer tempFile.Close() + + // Copy file ke direktori sementara + _, err = io.Copy(tempFile, src) + if err != nil { + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-2"). + Interface("err", err).Msg("") + } +} + +func uploadToMinIO(ctx context.Context, log zerolog.Logger, minioClient *minio.Client, uploadID, filePath, bucketName string, objectName string, fileSize int64) { + file, err := os.Open(filePath) + if err != nil { + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-3"). + Interface("err", err).Msg("") + return + } + defer file.Close() + + // Upload file ke MinIO dengan progress tracking + uploadProgress := int64(0) + reader := io.TeeReader(file, &progressWriter{uploadID: uploadID, totalSize: fileSize, uploadedSize: &uploadProgress}) + + _, err = minioClient.PutObject(ctx, bucketName, objectName, reader, fileSize, minio.PutObjectOptions{}) + if err != nil { + + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-4"). + Interface("err", err).Msg("") + return + } + + // Upload selesai, update progress menjadi 100 + progressLock.Lock() + progressMap[uploadID] = 100 + progressLock.Unlock() + + go removeFileTemp(log, filePath) +} + +func removeFileTemp(log zerolog.Logger, filePath string) { + err := os.Remove(filePath) + if err != nil { + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-5"). + Interface("Failed to remove temporary file", err).Msg("") + } else { + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:uploadToMinIO-6"). + Interface("err", "Temporary file removed").Msg("") + } +} + +func (p *progressWriter) Write(data []byte) (int, error) { + n := len(data) + progressLock.Lock() + defer progressLock.Unlock() + + *p.uploadedSize += int64(n) + progress := int(float64(*p.uploadedSize) / float64(p.totalSize) * 100) + + // Update progress di map + progressMap[p.uploadID] = progress + + return n, nil +} + +func (_i *articleFilesService) GetUploadStatus(c *fiber.Ctx) (progress int, err error) { + uploadID := c.Params("uploadId") + + // Ambil progress dari map + progressLock.Lock() + progress, _ = progressMap[uploadID] + progressLock.Unlock() + + return progress, nil +} diff --git a/app/module/article_files/service/async_uploader.service.go b/app/module/article_files/service/async_uploader.service.go new file mode 100644 index 0000000..17c627b --- /dev/null +++ b/app/module/article_files/service/async_uploader.service.go @@ -0,0 +1,139 @@ +package service + +import ( + "context" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" + "io" + "time" +) + +// AsyncUploader menangani proses upload secara asynchronous +type UploadService interface { + UploadFile(ctx context.Context, minioClient *minio.Client, uploadID string, reader io.Reader, bucketName string, objectName string, size int64, contentType string) error +} + +type uploadService struct { + uploadManager UploadManager + Log zerolog.Logger +} + +func NewUploadService(uploadManager UploadManager, log zerolog.Logger) UploadService { + return &uploadService{ + uploadManager: uploadManager, + Log: log, + } +} + +func (u *uploadService) UploadFile(ctx context.Context, minioClient *minio.Client, uploadID string, reader io.Reader, bucketName string, objectName string, size int64, contentType string) error { + status := &UploadStatus{ + FileName: objectName, + Size: size, + Progress: 0, + Status: "uploading", + ObjectName: objectName, + BucketName: bucketName, + StartTime: time.Now(), + } + + u.uploadManager.Add(uploadID, status) + + u.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "UploadService::UploadFile"). + Interface("add status", status).Msg("") + + // Upload ke Minio + _, err := minioClient.PutObject( + ctx, + bucketName, + objectName, + reader, + size, + minio.PutObjectOptions{ + ContentType: contentType, + PartSize: 10 * 1024 * 1024, // 10MB part size + }, + ) + + if err != nil { + u.uploadManager.UpdateStatus(uploadID, "error", err) + + u.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "UploadService::UploadFile"). + Interface("error when upload", err).Msg("") + } + + u.uploadManager.UpdateStatus(uploadID, "completed", nil) + return nil +} + +//func (au *UploadService) UploadFile() { +// // Buat context dengan timeout +// ctx, cancel := context.WithTimeout(au.ctx, 30*time.Minute) +// defer cancel() +// +// // Buat reader dari byte slice +// reader := bytes.NewReader(au.fileData) +// pipeReader, pipeWriter := io.Pipe() +// +// au.progressMap.Store(au.uploadID, 0.0) +// +// // Start goroutine to read from reader and write to pipe +// go func() { +// defer pipeWriter.Close() +// buf := make([]byte, au.partSize) +// +// totalParts := int(reader.Size() / au.partSize) +// if reader.Size()%au.partSize != 0 { +// totalParts++ +// } +// +// for i := 0; i < totalParts; i++ { +// n, err := reader.Read(buf) +// if err != nil && err != io.EOF { +// log.Println("Error reading file:", err) +// return +// } +// +// if _, err := pipeWriter.Write(buf[:n]); err != nil { +// log.Println("Error writing to pipe:", err) +// return +// } +// +// progress := float64(i+1) / float64(totalParts) * 100 +// au.progressMap.Store(au.uploadID, progress) +// au.uploadManager.UpdateProgress(au.uploadID, int(progress)) +// } +// }() +// +// // Upload ke Minio +// _, err := au.minioClient.PutObject( +// ctx, +// au.bucketName, +// au.objectName, +// pipeReader, +// reader.Size(), +// minio.PutObjectOptions{ +// ContentType: au.contentType, +// PartSize: 10 * 1024 * 1024, // 10MB part size +// }, +// ) +// +// if err != nil { +// log.Println("Error uploading file:", err) +// au.progressMap.Store(au.uploadID, "error") +// return +// } +// +// fmt.Printf("Uploading process for %s", au.objectName) +// +// if err != nil { +// uploadManager.UpdateStatus(au.uploadID, "error", err) +// fmt.Printf("Upload error for %s: %v\n", au.objectName, err) +// return +// } +// +// au.progressMap.Store(au.uploadID, 100) +// au.uploadManager.UpdateProgress(au.uploadID, 100) +// au.uploadManager.UpdateStatus(au.uploadID, "completed", nil) +//} diff --git a/app/module/article_files/service/upload_manager.service.go b/app/module/article_files/service/upload_manager.service.go new file mode 100644 index 0000000..f02ec96 --- /dev/null +++ b/app/module/article_files/service/upload_manager.service.go @@ -0,0 +1,71 @@ +package service + +import ( + "sync" + "time" +) + +type UploadStatus struct { + FileName string `json:"fileName"` + Size int64 `json:"size"` + Progress int `json:"progress"` + Status string `json:"status"` + ObjectName string `json:"objectName"` + BucketName string `json:"bucketName"` + StartTime time.Time `json:"startTime"` + Error string `json:"error,omitempty"` +} + +type UploadManager interface { + Add(uploadID string, status *UploadStatus) + UpdateProgress(uploadID string, progress int) + UpdateStatus(uploadID string, status string, err error) + Get(uploadID string) (*UploadStatus, bool) +} + +type uploadManager struct { + uploads map[string]*UploadStatus + mutex sync.RWMutex +} + +func NewUploadManager() UploadManager { + return &uploadManager{ + uploads: make(map[string]*UploadStatus), + } +} + +// Add menambahkan status upload baru +func (um *uploadManager) Add(uploadID string, status *UploadStatus) { + um.mutex.Lock() + defer um.mutex.Unlock() + um.uploads[uploadID] = status +} + +// UpdateProgress memperbarui progress upload +func (um *uploadManager) UpdateProgress(uploadID string, progress int) { + um.mutex.Lock() + defer um.mutex.Unlock() + if status, exists := um.uploads[uploadID]; exists { + status.Progress = progress + } +} + +// UpdateStatus memperbarui status upload +func (um *uploadManager) UpdateStatus(uploadID string, status string, err error) { + um.mutex.Lock() + defer um.mutex.Unlock() + if upload, exists := um.uploads[uploadID]; exists { + upload.Status = status + if err != nil { + upload.Error = err.Error() + } + } +} + +// Get mendapatkan status upload berdasarkan ID +func (um *uploadManager) Get(uploadID string) (*UploadStatus, bool) { + um.mutex.RLock() + defer um.mutex.RUnlock() + status, exists := um.uploads[uploadID] + return status, exists +} diff --git a/app/module/articles/articles.module.go b/app/module/articles/articles.module.go new file mode 100644 index 0000000..79100ea --- /dev/null +++ b/app/module/articles/articles.module.go @@ -0,0 +1,62 @@ +package articles + +import ( + "narasi-ahli-be/app/module/articles/controller" + "narasi-ahli-be/app/module/articles/repository" + "narasi-ahli-be/app/module/articles/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// ArticlesRouter struct of ArticlesRouter +type ArticlesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// NewArticlesModule register bulky of Articles module +var NewArticlesModule = fx.Options( + // register repository of Articles module + fx.Provide(repository.NewArticlesRepository), + + // register service of Articles module + fx.Provide(service.NewArticlesService), + + // register controller of Articles module + fx.Provide(controller.NewController), + + // register router of Articles module + fx.Provide(NewArticlesRouter), +) + +// NewArticlesRouter init ArticlesRouter +func NewArticlesRouter(fiber *fiber.App, controller *controller.Controller) *ArticlesRouter { + return &ArticlesRouter{ + App: fiber, + Controller: controller, + } +} + +// RegisterArticlesRoutes register routes of Articles module +func (_i *ArticlesRouter) RegisterArticlesRoutes() { + // define controllers + articlesController := _i.Controller.Articles + + // define routes + _i.App.Route("/articles", func(router fiber.Router) { + router.Get("/", articlesController.All) + router.Get("/old-id/:id", articlesController.ShowByOldId) + router.Get("/:id", articlesController.Show) + router.Post("/", articlesController.Save) + router.Put("/:id", articlesController.Update) + router.Put("/banner/:id", articlesController.UpdateBanner) + router.Post("/thumbnail/:id", articlesController.SaveThumbnail) + router.Get("/thumbnail/viewer/:thumbnailName", articlesController.Viewer) + router.Post("/publish-scheduling", articlesController.PublishScheduling) + router.Delete("/:id", articlesController.Delete) + router.Get("/statistic/summary", articlesController.SummaryStats) + router.Get("/statistic/user-levels", articlesController.ArticlePerUserLevelStats) + router.Get("/statistic/monthly", articlesController.ArticleMonthlyStats) + }) +} diff --git a/app/module/articles/controller/articles.controller.go b/app/module/articles/controller/articles.controller.go new file mode 100644 index 0000000..5b61ba5 --- /dev/null +++ b/app/module/articles/controller/articles.controller.go @@ -0,0 +1,454 @@ +package controller + +import ( + "narasi-ahli-be/app/module/articles/request" + "narasi-ahli-be/app/module/articles/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type articlesController struct { + articlesService service.ArticlesService + Log zerolog.Logger +} + +type ArticlesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + ShowByOldId(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + SaveThumbnail(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + UpdateBanner(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error + SummaryStats(c *fiber.Ctx) error + ArticlePerUserLevelStats(c *fiber.Ctx) error + ArticleMonthlyStats(c *fiber.Ctx) error + PublishScheduling(c *fiber.Ctx) error +} + +func NewArticlesController(articlesService service.ArticlesService, log zerolog.Logger) ArticlesController { + return &articlesController{ + articlesService: articlesService, + Log: log, + } +} + +// All Articles +// @Summary Get all Articles +// @Description API for getting all Articles +// @Tags Articles +// @Security Bearer +// @Param X-Client-Key header string true "Insert the X-Client-Key" +// @Param req query request.ArticlesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles [get] +func (_i *articlesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.ArticlesQueryRequestContext{ + Title: c.Query("title"), + Description: c.Query("description"), + Tags: c.Query("tags"), + Category: c.Query("category"), + CategoryId: c.Query("categoryId"), + TypeId: c.Query("typeId"), + StatusId: c.Query("statusId"), + IsPublish: c.Query("isPublish"), + IsDraft: c.Query("isDraft"), + IsBanner: c.Query("isBanner"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + articlesData, paging, err := _i.articlesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles list successfully retrieved"}, + Data: articlesData, + Meta: paging, + }) +} + +// Show Articles +// @Summary Get one Articles +// @Description API for getting one Articles +// @Tags Articles +// @Security Bearer +// @Param id path int true "Articles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/{id} [get] +func (_i *articlesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + // Get from context + articlesData, err := _i.articlesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles successfully retrieved"}, + Data: articlesData, + }) +} + +// ShowByOldId Articles +// @Summary Get one Articles +// @Description API for getting one Articles +// @Tags Articles +// @Security Bearer +// @Param id path int true "Articles Old ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/old-id/{id} [get] +func (_i *articlesController) ShowByOldId(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + // Get from context + articlesData, err := _i.articlesService.ShowByOldId(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles successfully retrieved"}, + Data: articlesData, + }) +} + +// Save Articles +// @Summary Create Articles +// @Description API for create Articles +// @Tags Articles +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ArticlesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles [post] +func (_i *articlesController) Save(c *fiber.Ctx) error { + req := new(request.ArticlesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + // Get from context + dataResult, err := _i.articlesService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles successfully created"}, + Data: dataResult, + }) +} + +// SaveThumbnail Articles +// @Summary Save Thumbnail Articles +// @Description API for Save Thumbnail of Articles +// @Tags Articles +// @Security Bearer +// @Produce json +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param files formData file true "Upload thumbnail" +// @Param id path int true "Articles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/thumbnail/{id} [post] +func (_i *articlesController) SaveThumbnail(c *fiber.Ctx) error { + // Get from context + err := _i.articlesService.SaveThumbnail(c) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Thumbnail of Articles successfully created"}, + }) +} + +// Update Articles +// @Summary Update Articles +// @Description API for update Articles +// @Tags Articles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.ArticlesUpdateRequest true "Required payload" +// @Param id path int true "Articles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/{id} [put] +func (_i *articlesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ArticlesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + // Get from context + err = _i.articlesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles successfully updated"}, + }) +} + +// UpdateBanner Articles +// @Summary Update Banner Articles +// @Description API for Update Banner Articles +// @Tags Articles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Articles ID" +// @Param isBanner query bool true "Articles Banner Status" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/banner/{id} [put] +func (_i *articlesController) UpdateBanner(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + isBanner, err := strconv.ParseBool(c.Query("isBanner")) + if err != nil { + return err + } + + // Get from context + err = _i.articlesService.UpdateBanner(uint(id), isBanner) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles successfully banner updated"}, + }) +} + +// Delete Articles +// @Summary Delete Articles +// @Description API for delete Articles +// @Tags Articles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Articles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/{id} [delete] +func (_i *articlesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + // Get from context + err = _i.articlesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Articles successfully deleted"}, + }) +} + +// Viewer Articles +// @Summary Viewer Articles Thumbnail +// @Description API for View Thumbnail of Article +// @Tags Articles +// @Security Bearer +// @Param thumbnailName path string true "Articles Thumbnail Name" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/thumbnail/viewer/{thumbnailName} [get] +func (_i *articlesController) Viewer(c *fiber.Ctx) error { + // Get from context + return _i.articlesService.Viewer(c) +} + +// SummaryStats Articles +// @Summary SummaryStats Articles +// @Description API for Summary Stats of Article +// @Tags Articles +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/statistic/summary [get] +func (_i *articlesController) SummaryStats(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + + // Get from context + response, err := _i.articlesService.SummaryStats(authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Summary Stats of Articles successfully retrieved"}, + Data: response, + }) +} + +// ArticlePerUserLevelStats Articles +// @Summary ArticlePerUserLevelStats Articles +// @Description API for ArticlePerUserLevelStats of Article +// @Tags Articles +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param startDate query string false "start date" +// @Param endDate query string false "start date" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/statistic/user-levels [get] +func (_i *articlesController) ArticlePerUserLevelStats(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + startDate := c.Query("startDate") + endDate := c.Query("endDate") + + // Get from context + response, err := _i.articlesService.ArticlePerUserLevelStats(authToken, &startDate, &endDate) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticlePerUserLevelStats of Articles successfully retrieved"}, + Data: response, + }) +} + +// ArticleMonthlyStats Articles +// @Summary ArticleMonthlyStats Articles +// @Description API for ArticleMonthlyStats of Article +// @Tags Articles +// @Security Bearer +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param year query int false "year" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/statistic/monthly [get] +func (_i *articlesController) ArticleMonthlyStats(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + year := c.Query("year") + yearInt, err := strconv.Atoi(year) + if err != nil { + return err + } + + // Get from context + response, err := _i.articlesService.ArticleMonthlyStats(authToken, &yearInt) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"ArticleMonthlyStats of Articles successfully retrieved"}, + Data: response, + }) +} + +// PublishScheduling Articles +// @Summary PublishScheduling Articles +// @Description API for Publish Schedule of Article +// @Tags Articles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param id query int false "article id" +// @Param date query string false "publish date" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /articles/publish-scheduling [post] +func (_i *articlesController) PublishScheduling(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Query("id"), 10, 0) + if err != nil { + return err + } + date := c.Query("date") + + // Get from context + err = _i.articlesService.PublishScheduling(uint(id), date) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Publish Scheduling of Articles successfully saved"}, + }) +} diff --git a/app/module/articles/controller/controller.go b/app/module/articles/controller/controller.go new file mode 100644 index 0000000..6fed97a --- /dev/null +++ b/app/module/articles/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/articles/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + Articles ArticlesController +} + +func NewController(ArticlesService service.ArticlesService, log zerolog.Logger) *Controller { + return &Controller{ + Articles: NewArticlesController(ArticlesService, log), + } +} diff --git a/app/module/articles/mapper/articles.mapper.go b/app/module/articles/mapper/articles.mapper.go new file mode 100644 index 0000000..32e2e95 --- /dev/null +++ b/app/module/articles/mapper/articles.mapper.go @@ -0,0 +1,90 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + articleCategoriesMapper "narasi-ahli-be/app/module/article_categories/mapper" + articleCategoriesRepository "narasi-ahli-be/app/module/article_categories/repository" + articleCategoriesResponse "narasi-ahli-be/app/module/article_categories/response" + articleCategoryDetailsRepository "narasi-ahli-be/app/module/article_category_details/repository" + articleFilesMapper "narasi-ahli-be/app/module/article_files/mapper" + articleFilesRepository "narasi-ahli-be/app/module/article_files/repository" + articleFilesResponse "narasi-ahli-be/app/module/article_files/response" + res "narasi-ahli-be/app/module/articles/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + + "github.com/rs/zerolog" +) + +func ArticlesResponseMapper( + log zerolog.Logger, + host string, + articlesReq *entity.Articles, + articleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository, + articleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository, + articleFilesRepo articleFilesRepository.ArticleFilesRepository, + usersRepo usersRepository.UsersRepository, +) (articlesRes *res.ArticlesResponse) { + + createdByName := "" + if articlesReq.CreatedById != nil { + findUser, _ := usersRepo.FindOne(*articlesReq.CreatedById) + if findUser != nil { + createdByName = findUser.Fullname + } + } + + categoryName := "" + articleCategories, _ := articleCategoryDetailsRepo.FindByArticleId(articlesReq.ID) + var articleCategoriesArr []*articleCategoriesResponse.ArticleCategoriesResponse + if articleCategories != nil && len(articleCategories) > 0 { + for _, result := range articleCategories { + articleCategoriesArr = append(articleCategoriesArr, articleCategoriesMapper.ArticleCategoriesResponseMapper(result.Category, host)) + } + log.Info().Interface("articleCategoriesArr", articleCategoriesArr).Msg("") + } + + articleFiles, _ := articleFilesRepo.FindByArticle(articlesReq.ID) + var articleFilesArr []*articleFilesResponse.ArticleFilesResponse + if articleFiles != nil && len(articleFiles) > 0 { + for _, result := range articleFiles { + articleFilesArr = append(articleFilesArr, articleFilesMapper.ArticleFilesResponseMapper(result, host)) + } + } + + if articlesReq != nil { + articlesRes = &res.ArticlesResponse{ + ID: articlesReq.ID, + Title: articlesReq.Title, + Slug: articlesReq.Slug, + Description: articlesReq.Description, + HtmlDescription: articlesReq.HtmlDescription, + TypeId: articlesReq.TypeId, + Tags: articlesReq.Tags, + CategoryId: articlesReq.CategoryId, + AiArticleId: articlesReq.AiArticleId, + CategoryName: categoryName, + PageUrl: articlesReq.PageUrl, + CreatedById: articlesReq.CreatedById, + CreatedByName: &createdByName, + ShareCount: articlesReq.ShareCount, + ViewCount: articlesReq.ViewCount, + CommentCount: articlesReq.CommentCount, + OldId: articlesReq.OldId, + StatusId: articlesReq.StatusId, + IsBanner: articlesReq.IsBanner, + IsPublish: articlesReq.IsPublish, + PublishedAt: articlesReq.PublishedAt, + IsActive: articlesReq.IsActive, + CreatedAt: articlesReq.CreatedAt, + UpdatedAt: articlesReq.UpdatedAt, + ArticleFiles: articleFilesArr, + ArticleCategories: articleCategoriesArr, + } + + if articlesReq.ThumbnailName != nil { + articlesRes.ThumbnailUrl = host + "/articles/thumbnail/viewer/" + *articlesReq.ThumbnailName + } + } + + return articlesRes +} diff --git a/app/module/articles/repository/articles.repository.go b/app/module/articles/repository/articles.repository.go new file mode 100644 index 0000000..26839d8 --- /dev/null +++ b/app/module/articles/repository/articles.repository.go @@ -0,0 +1,330 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/articles/request" + "narasi-ahli-be/app/module/articles/response" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + "time" + + "github.com/rs/zerolog" +) + +type articlesRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// ArticlesRepository define interface of IArticlesRepository +type ArticlesRepository interface { + GetAll(req request.ArticlesQueryRequest) (articless []*entity.Articles, paging paginator.Pagination, err error) + GetAllPublishSchedule() (articless []*entity.Articles, err error) + FindOne(id uint) (articles *entity.Articles, err error) + FindByFilename(thumbnailName string) (articleReturn *entity.Articles, err error) + FindByOldId(oldId uint) (articles *entity.Articles, err error) + Create(articles *entity.Articles) (articleReturn *entity.Articles, err error) + Update(id uint, articles *entity.Articles) (err error) + UpdateSkipNull(id uint, articles *entity.Articles) (err error) + Delete(id uint) (err error) + SummaryStats(userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error) + ArticlePerUserLevelStats(userLevelId *uint, levelNumber *int, startDate *time.Time, endDate *time.Time) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) + ArticleMonthlyStats(userLevelId *uint, levelNumber *int, year int) (articleMontlyStats []*response.ArticleMonthlyStats, err error) +} + +func NewArticlesRepository(db *database.Database, log zerolog.Logger) ArticlesRepository { + return &articlesRepository{ + DB: db, + Log: log, + } +} + +// implement interface of IArticlesRepository +func (_i *articlesRepository) GetAll(req request.ArticlesQueryRequest) (articless []*entity.Articles, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Articles{}) + + if req.CategoryId != nil { + query = query.Joins("JOIN article_category_details acd ON acd.article_id = articles.id"). + Where("acd.category_id = ?", req.CategoryId) + } + query = query.Where("articles.is_active = ?", true) + + if req.Title != nil && *req.Title != "" { + title := strings.ToLower(*req.Title) + query = query.Where("LOWER(articles.title) LIKE ?", "%"+strings.ToLower(title)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(articles.description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.Tags != nil && *req.Tags != "" { + tags := strings.ToLower(*req.Tags) + query = query.Where("LOWER(articles.tags) LIKE ?", "%"+strings.ToLower(tags)+"%") + } + if req.TypeId != nil { + query = query.Where("articles.type_id = ?", req.TypeId) + } + if req.IsPublish != nil { + query = query.Where("articles.is_publish = ?", req.IsPublish) + } + if req.IsBanner != nil { + query = query.Where("articles.is_banner = ?", req.IsBanner) + } + if req.IsDraft != nil { + query = query.Where("articles.is_draft = ?", req.IsDraft) + } + if req.StatusId != nil { + query = query.Where("articles.status_id = ?", req.StatusId) + } + if req.CreatedById != nil { + query = query.Where("articles.created_by_id = ?", req.CreatedById) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } else { + direction := "DESC" + sortBy := "articles.created_at" + query.Order(fmt.Sprintf("%s %s", sortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articless).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *articlesRepository) GetAllPublishSchedule() (articles []*entity.Articles, err error) { + query := _i.DB.DB.Where("publish_schedule IS NOT NULL") + err = query.Find(&articles).Error + if err != nil { + return nil, err + } + return articles, nil +} + +func (_i *articlesRepository) FindOne(id uint) (articles *entity.Articles, err error) { + query := _i.DB.DB + if err := query.First(&articles, id).Error; err != nil { + return nil, err + } + + return articles, nil +} + +func (_i *articlesRepository) FindByFilename(thumbnailName string) (articles *entity.Articles, err error) { + query := _i.DB.DB.Where("thumbnail_name = ?", thumbnailName) + + if err := query.First(&articles).Error; err != nil { + return nil, err + } + + return articles, nil +} + +func (_i *articlesRepository) FindByOldId(oldId uint) (articles *entity.Articles, err error) { + query := _i.DB.DB.Where("old_id = ?", oldId) + + if err := query.First(&articles).Error; err != nil { + return nil, err + } + + return articles, nil +} + +func (_i *articlesRepository) Create(articles *entity.Articles) (articleReturn *entity.Articles, err error) { + result := _i.DB.DB.Create(articles) + return articles, result.Error +} + +func (_i *articlesRepository) Update(id uint, articles *entity.Articles) (err error) { + articlesMap, err := utilSvc.StructToMap(articles) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.Articles{}). + Where(&entity.Articles{ID: id}). + Updates(articlesMap).Error +} + +func (_i *articlesRepository) UpdateSkipNull(id uint, articles *entity.Articles) (err error) { + return _i.DB.DB.Model(&entity.Articles{}). + Where(&entity.Articles{ID: id}). + Updates(articles).Error +} + +func (_i *articlesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.Articles{}, id).Error +} + +func (_i *articlesRepository) SummaryStats(userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error) { + now := time.Now() + startOfDay := now.Truncate(24 * time.Hour) + startOfWeek := now.AddDate(0, 0, -int(now.Weekday())+1).Truncate(24 * time.Hour) + + // Query + query := _i.DB.DB.Model(&entity.Articles{}). + Select( + "COUNT(*) AS total_all, "+ + "COALESCE(SUM(view_count), 0) AS total_views, "+ + "COALESCE(SUM(share_count), 0) AS total_shares, "+ + "COALESCE(SUM(comment_count), 0) AS total_comments, "+ + "COUNT(CASE WHEN created_at >= ? THEN 1 END) AS total_today, "+ + "COUNT(CASE WHEN created_at >= ? THEN 1 END) AS total_this_week", + startOfDay, startOfWeek). + Where("created_by_id = ?", userID) + + err = query.Scan(&articleSummaryStats).Error + + return articleSummaryStats, err +} + +func (_i *articlesRepository) ArticlePerUserLevelStats(userLevelId *uint, levelNumber *int, startDate *time.Time, endDate *time.Time) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) { + + levelNumberTop := 1 + + query := _i.DB.DB.Model(&entity.Articles{}). + Select("user_levels.id as user_level_id", "user_levels.name as user_level_name", "COUNT(articles.id) as total_article"). + Joins("LEFT JOIN users ON articles.created_by_id = users.id"). + Joins("LEFT JOIN user_levels ON users.user_level_id = user_levels.id"). + Where("articles.is_active = true") + + if userLevelId != nil && *levelNumber != levelNumberTop { + query = query.Where("user_levels.id = ? or user_levels.parent_level_id = ?", *userLevelId, *userLevelId) + } else { + query = _i.DB.DB.Raw(` + WITH LevelHierarchy AS ( + SELECT + id, + name, + level_number, + parent_level_id, + CASE + WHEN level_number = 1 THEN id + WHEN level_number = 2 and name ILIKE '%polda%' THEN id + WHEN level_number = 2 and name NOT ILIKE '%polda%' THEN parent_level_id + WHEN level_number = 3 THEN parent_level_id + END AS level_2_id, + CASE + WHEN level_number = 1 THEN name + WHEN level_number = 2 and name ILIKE '%polda%' THEN name + WHEN level_number = 2 and name NOT ILIKE '%polda%' THEN (SELECT name FROM user_levels ul2 WHERE ul2.id = user_levels.parent_level_id) + WHEN level_number = 3 THEN (SELECT name FROM user_levels ul2 WHERE ul2.id = user_levels.parent_level_id) + END AS level_2_name + FROM user_levels + ) + SELECT + lh.level_2_id AS user_level_id, + UPPER(lh.level_2_name) AS user_level_name, + COUNT(articles.id) AS total_article + FROM articles + JOIN users ON articles.created_by_id = users.id + JOIN LevelHierarchy lh ON users.user_level_id = lh.id + WHERE articles.is_active = true AND lh.level_2_id > 0`) + + query = query.Group("lh.level_2_id, lh.level_2_name").Order("total_article DESC") + } + + // Apply date filters if provided + if startDate != nil { + query = query.Where("articles.created_at >= ?", *startDate) + } + if endDate != nil { + query = query.Where("articles.created_at <= ?", *endDate) + } + + // Group by all non-aggregated columns + err = query.Group("user_levels.id, user_levels.name"). + Order("total_article DESC"). + Scan(&articlePerUserLevelStats).Error + + return articlePerUserLevelStats, err +} + +func (_i *articlesRepository) ArticleMonthlyStats(userLevelId *uint, levelNumber *int, year int) (articleMontlyStats []*response.ArticleMonthlyStats, err error) { + levelNumberTop := 1 + + if year < 1900 || year > 2100 { + return nil, fmt.Errorf("invalid year") + } + + var results []struct { + Month int + Day int + TotalView int + TotalComment int + TotalShare int + } + + query := _i.DB.DB.Model(&entity.Articles{}). + Select("EXTRACT(MONTH FROM created_at) as month, EXTRACT(DAY FROM created_at) as day, "+ + "SUM(view_count) as total_view, "+ + "SUM(comment_count) as total_comment, "+ + "SUM(share_count) as total_share"). + Where("EXTRACT(YEAR FROM created_at) = ?", year) + + if userLevelId != nil && *levelNumber != levelNumberTop { + query = _i.DB.DB.Model(&entity.Articles{}). + Select("EXTRACT(MONTH FROM articles.created_at) as month, EXTRACT(DAY FROM articles.created_at) as day, "+ + "SUM(articles.view_count) as total_view, "+ + "SUM(articles.comment_count) as total_comment, "+ + "SUM(articles.share_count) as total_share"). + Joins("LEFT JOIN users ON articles.created_by_id = users.id"). + Joins("LEFT JOIN user_levels ON users.user_level_id = user_levels.id"). + Where("articles.is_active = true"). + Where("EXTRACT(YEAR FROM articles.created_at) = ?", year). + Where("(user_levels.id = ? OR user_levels.parent_level_id = ?)", *userLevelId, *userLevelId) + + } + + err = query.Group("month, day").Scan(&results).Error + if err != nil { + return nil, err + } + + // Siapkan struktur untuk menyimpan data bulanan + monthlyAnalytics := make([]*response.ArticleMonthlyStats, 12) + for i := 0; i < 12; i++ { + daysInMonth := time.Date(year, time.Month(i+1), 0, 0, 0, 0, 0, time.UTC).Day() + monthlyAnalytics[i] = &response.ArticleMonthlyStats{ + Year: year, + Month: i + 1, + View: make([]int, daysInMonth), + Comment: make([]int, daysInMonth), + Share: make([]int, daysInMonth), + } + } + + // Isi data dari hasil agregasi + for _, result := range results { + monthIndex := result.Month - 1 + dayIndex := result.Day - 1 + + if monthIndex >= 0 && monthIndex < 12 { + if dayIndex >= 0 && dayIndex < len(monthlyAnalytics[monthIndex].View) { + monthlyAnalytics[monthIndex].View[dayIndex] = result.TotalView + monthlyAnalytics[monthIndex].Comment[dayIndex] = result.TotalComment + monthlyAnalytics[monthIndex].Share[dayIndex] = result.TotalShare + } + } + } + + return monthlyAnalytics, nil +} diff --git a/app/module/articles/request/articles.request.go b/app/module/articles/request/articles.request.go new file mode 100644 index 0000000..1b70af1 --- /dev/null +++ b/app/module/articles/request/articles.request.go @@ -0,0 +1,183 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type ArticlesGeneric interface { + ToEntity() +} + +type ArticlesQueryRequest struct { + Title *string `json:"title"` + Description *string `json:"description"` + CategoryId *uint `json:"categoryId"` + Category *string `json:"category"` + TypeId *int `json:"typeId"` + Tags *string `json:"tags"` + CreatedById *int `json:"createdById"` + StatusId *int `json:"statusId"` + IsBanner *bool `json:"isBanner"` + IsPublish *bool `json:"isPublish"` + IsDraft *bool `json:"isDraft"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ArticlesCreateRequest struct { + Title string `json:"title" validate:"required"` + Slug string `json:"slug" validate:"required"` + Description string `json:"description" validate:"required"` + HtmlDescription string `json:"htmlDescription" validate:"required"` + CategoryIds string `json:"categoryIds" validate:"required"` + TypeId int `json:"typeId" validate:"required"` + Tags string `json:"tags" validate:"required"` + AiArticleId *int `json:"aiArticleId"` + CreatedAt *string `json:"createdAt"` + CreatedById *uint `json:"createdById"` + IsPublish *bool `json:"isPublish"` + IsDraft *bool `json:"isDraft"` + OldId *uint `json:"oldId"` +} + +func (req ArticlesCreateRequest) ToEntity() *entity.Articles { + return &entity.Articles{ + Title: req.Title, + Slug: req.Slug, + Description: req.Description, + HtmlDescription: req.HtmlDescription, + TypeId: req.TypeId, + Tags: req.Tags, + AiArticleId: req.AiArticleId, + IsPublish: req.IsPublish, + IsDraft: req.IsDraft, + OldId: req.OldId, + } +} + +type ArticlesUpdateRequest struct { + Title string `json:"title" validate:"required"` + Slug string `json:"slug" validate:"required"` + Description string `json:"description" validate:"required"` + HtmlDescription string `json:"htmlDescription" validate:"required"` + CategoryIds string `json:"categoryIds" validate:"required"` + TypeId int `json:"typeId" validate:"required"` + Tags string `json:"tags" validate:"required"` + CreatedAt *string `json:"createdAt"` + CreatedById *uint `json:"createdById"` + AiArticleId *int `json:"aiArticleId"` + IsPublish *bool `json:"isPublish"` + IsDraft *bool `json:"isDraft"` + StatusId *int `json:"statusId"` +} + +func (req ArticlesUpdateRequest) ToEntity() *entity.Articles { + if req.CreatedById == nil { + return &entity.Articles{ + Title: req.Title, + Slug: req.Slug, + Description: req.Description, + HtmlDescription: req.HtmlDescription, + TypeId: req.TypeId, + Tags: req.Tags, + StatusId: req.StatusId, + AiArticleId: req.AiArticleId, + IsPublish: req.IsPublish, + IsDraft: req.IsDraft, + UpdatedAt: time.Now(), + } + } else { + return &entity.Articles{ + Title: req.Title, + Slug: req.Slug, + Description: req.Description, + HtmlDescription: req.HtmlDescription, + TypeId: req.TypeId, + Tags: req.Tags, + StatusId: req.StatusId, + CreatedById: req.CreatedById, + AiArticleId: req.AiArticleId, + IsPublish: req.IsPublish, + IsDraft: req.IsDraft, + UpdatedAt: time.Now(), + } + } +} + +type ArticlesQueryRequestContext struct { + Title string `json:"title"` + Description string `json:"description"` + CategoryId string `json:"categoryId"` + Category string `json:"category"` + TypeId string `json:"typeId"` + Tags string `json:"tags"` + CreatedById string `json:"createdById"` + IsBanner string `json:"isBanner"` + IsPublish string `json:"isPublish"` + IsDraft string `json:"isDraft"` + StatusId string `json:"statusId"` +} + +func (req ArticlesQueryRequestContext) ToParamRequest() ArticlesQueryRequest { + var request ArticlesQueryRequest + + if title := req.Title; title != "" { + request.Title = &title + } + if description := req.Description; description != "" { + request.Description = &description + } + if category := req.Category; category != "" { + request.Category = &category + } + if categoryIdStr := req.CategoryId; categoryIdStr != "" { + categoryId, err := strconv.Atoi(categoryIdStr) + if err == nil { + categoryIdUint := uint(categoryId) + request.CategoryId = &categoryIdUint + } + } + if typeIdStr := req.TypeId; typeIdStr != "" { + typeId, err := strconv.Atoi(typeIdStr) + if err == nil { + request.TypeId = &typeId + } + } + if tags := req.Tags; tags != "" { + request.Tags = &tags + } + if isPublishStr := req.IsPublish; isPublishStr != "" { + isPublish, err := strconv.ParseBool(isPublishStr) + if err == nil { + request.IsPublish = &isPublish + } + } + if isBannerStr := req.IsBanner; isBannerStr != "" { + isBanner, err := strconv.ParseBool(isBannerStr) + if err == nil { + request.IsBanner = &isBanner + } + } + if isDraftStr := req.IsDraft; isDraftStr != "" { + isDraft, err := strconv.ParseBool(isDraftStr) + if err == nil { + request.IsDraft = &isDraft + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + if createdByIdStr := req.CreatedById; createdByIdStr != "" { + createdById, err := strconv.Atoi(createdByIdStr) + if err == nil { + request.CreatedById = &createdById + } + } + + return request +} diff --git a/app/module/articles/response/articles.response.go b/app/module/articles/response/articles.response.go new file mode 100644 index 0000000..9e6b7c5 --- /dev/null +++ b/app/module/articles/response/articles.response.go @@ -0,0 +1,61 @@ +package response + +import ( + articleCategoriesResponse "narasi-ahli-be/app/module/article_categories/response" + articleFilesResponse "narasi-ahli-be/app/module/article_files/response" + "time" +) + +type ArticlesResponse struct { + ID uint `json:"id"` + Title string `json:"title"` + Slug string `json:"slug"` + Description string `json:"description"` + HtmlDescription string `json:"htmlDescription"` + CategoryId int `json:"categoryId"` + CategoryName string `json:"categoryName"` + TypeId int `json:"typeId"` + Tags string `json:"tags"` + ThumbnailUrl string `json:"thumbnailUrl"` + PageUrl *string `json:"pageUrl"` + CreatedById *uint `json:"createdById"` + CreatedByName *string `json:"createdByName"` + ShareCount *int `json:"shareCount"` + ViewCount *int `json:"viewCount"` + CommentCount *int `json:"commentCount"` + AiArticleId *int `json:"aiArticleId"` + OldId *uint `json:"oldId"` + StatusId *int `json:"statusId"` + IsBanner *bool `json:"isBanner"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + ArticleFiles []*articleFilesResponse.ArticleFilesResponse `json:"files"` + ArticleCategories []*articleCategoriesResponse.ArticleCategoriesResponse `json:"categories"` +} + +type ArticleSummaryStats struct { + TotalToday int `json:"totalToday"` + TotalThisWeek int `json:"totalThisWeek"` + TotalAll int `json:"totalAll"` + TotalViews int `json:"totalViews"` + TotalShares int `json:"totalShares"` + TotalComments int `json:"totalComments"` +} + +type ArticlePerUserLevelStats struct { + UserLevelID uint `json:"userLevelId"` + UserLevelName string `json:"userLevelName"` + TotalArticle int64 `json:"totalArticle"` +} + +type ArticleMonthlyStats struct { + Year int `json:"year"` + Month int `json:"month"` + View []int `json:"view"` + Comment []int `json:"comment"` + Share []int `json:"share"` +} diff --git a/app/module/articles/service/articles.service.go b/app/module/articles/service/articles.service.go new file mode 100644 index 0000000..7a8992c --- /dev/null +++ b/app/module/articles/service/articles.service.go @@ -0,0 +1,619 @@ +package service + +import ( + "context" + "errors" + "fmt" + "io" + "log" + "math/rand" + "mime" + "narasi-ahli-be/app/database/entity" + articleApprovalsRepository "narasi-ahli-be/app/module/article_approvals/repository" + articleCategoriesRepository "narasi-ahli-be/app/module/article_categories/repository" + articleCategoryDetailsRepository "narasi-ahli-be/app/module/article_category_details/repository" + articleCategoryDetailsReq "narasi-ahli-be/app/module/article_category_details/request" + articleFilesRepository "narasi-ahli-be/app/module/article_files/repository" + "narasi-ahli-be/app/module/articles/mapper" + "narasi-ahli-be/app/module/articles/repository" + "narasi-ahli-be/app/module/articles/request" + "narasi-ahli-be/app/module/articles/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +// ArticlesService +type articlesService struct { + Repo repository.ArticlesRepository + ArticleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository + ArticleFilesRepo articleFilesRepository.ArticleFilesRepository + ArticleApprovalsRepo articleApprovalsRepository.ArticleApprovalsRepository + ArticleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository + Log zerolog.Logger + Cfg *config.Config + UsersRepo usersRepository.UsersRepository + MinioStorage *minioStorage.MinioStorage +} + +// ArticlesService define interface of IArticlesService +type ArticlesService interface { + All(req request.ArticlesQueryRequest) (articles []*response.ArticlesResponse, paging paginator.Pagination, err error) + Show(id uint) (articles *response.ArticlesResponse, err error) + ShowByOldId(oldId uint) (articles *response.ArticlesResponse, err error) + Save(req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) + SaveThumbnail(c *fiber.Ctx) (err error) + Update(id uint, req request.ArticlesUpdateRequest) (err error) + Delete(id uint) error + UpdateActivityCount(id uint, activityTypeId int) (err error) + UpdateApproval(id uint, statusId int, userLevelId int, userLevelNumber int, userParentLevelId int) (err error) + UpdateBanner(id uint, isBanner bool) error + Viewer(c *fiber.Ctx) error + SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error) + ArticlePerUserLevelStats(authToken string, startDate *string, endDate *string) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) + ArticleMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error) + PublishScheduling(id uint, publishSchedule string) error +} + +// NewArticlesService init ArticlesService +func NewArticlesService( + repo repository.ArticlesRepository, + articleCategoriesRepo articleCategoriesRepository.ArticleCategoriesRepository, + articleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository, + articleFilesRepo articleFilesRepository.ArticleFilesRepository, + articleApprovalsRepo articleApprovalsRepository.ArticleApprovalsRepository, + log zerolog.Logger, + cfg *config.Config, + usersRepo usersRepository.UsersRepository, + minioStorage *minioStorage.MinioStorage) ArticlesService { + + return &articlesService{ + Repo: repo, + ArticleCategoriesRepo: articleCategoriesRepo, + ArticleCategoryDetailsRepo: articleCategoryDetailsRepo, + ArticleFilesRepo: articleFilesRepo, + ArticleApprovalsRepo: articleApprovalsRepo, + Log: log, + UsersRepo: usersRepo, + MinioStorage: minioStorage, + Cfg: cfg, + } +} + +// All implement interface of ArticlesService +func (_i *articlesService) All(req request.ArticlesQueryRequest) (articless []*response.ArticlesResponse, paging paginator.Pagination, err error) { + if req.Category != nil { + findCategory, err := _i.ArticleCategoriesRepo.FindOneBySlug(*req.Category) + if err != nil { + return nil, paging, err + } + req.CategoryId = &findCategory.ID + } + + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:articlesService", "Methods:All"). + Interface("results", results).Msg("") + + host := _i.Cfg.App.Domain + + for _, result := range results { + articleRes := mapper.ArticlesResponseMapper(_i.Log, host, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo) + articless = append(articless, articleRes) + } + + return +} + +func (_i *articlesService) Show(id uint) (articles *response.ArticlesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + host := _i.Cfg.App.Domain + + return mapper.ArticlesResponseMapper(_i.Log, host, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil +} + +func (_i *articlesService) ShowByOldId(oldId uint) (articles *response.ArticlesResponse, err error) { + result, err := _i.Repo.FindByOldId(oldId) + if err != nil { + return nil, err + } + + host := _i.Cfg.App.Domain + + return mapper.ArticlesResponseMapper(_i.Log, host, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil +} + +func (_i *articlesService) Save(req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + var userLevelNumber int + var userParentLevelId int + if req.CreatedById != nil { + createdBy, err := _i.UsersRepo.FindOne(*req.CreatedById) + if err != nil { + return nil, fmt.Errorf("User not found") + } + newReq.CreatedById = &createdBy.ID + userLevelNumber = createdBy.UserLevel.LevelNumber + if createdBy.UserLevel.ParentLevelId != nil { + userParentLevelId = *createdBy.UserLevel.ParentLevelId + } + } else { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + newReq.CreatedById = &createdBy.ID + userLevelNumber = createdBy.UserLevel.LevelNumber + if createdBy.UserLevel.ParentLevelId != nil { + userParentLevelId = *createdBy.UserLevel.ParentLevelId + } + } + + isDraft := true + if req.IsDraft == &isDraft { + draftedAt := time.Now() + newReq.IsDraft = &isDraft + newReq.DraftedAt = &draftedAt + isPublishFalse := false + newReq.IsPublish = &isPublishFalse + newReq.PublishedAt = nil + } + + isPublish := true + if req.IsPublish == &isPublish { + publishedAt := time.Now() + newReq.IsPublish = &isPublish + newReq.PublishedAt = &publishedAt + isDraftFalse := false + newReq.IsDraft = &isDraftFalse + newReq.DraftedAt = nil + } + + if req.CreatedAt != nil { + layout := "2006-01-02 15:04:05" + parsedTime, err := time.Parse(layout, *req.CreatedAt) + if err != nil { + return nil, fmt.Errorf("Error parsing time:", err) + } + newReq.CreatedAt = parsedTime + } + + // Approval + statusIdOne := 1 + statusIdTwo := 2 + isPublishFalse := false + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == false { + newReq.NeedApprovalFrom = nil + newReq.StatusId = &statusIdTwo + } else { + newReq.NeedApprovalFrom = &userParentLevelId + newReq.StatusId = &statusIdOne + newReq.IsPublish = &isPublishFalse + newReq.PublishedAt = nil + } + + saveArticleRes, err := _i.Repo.Create(newReq) + if err != nil { + return nil, err + } + + // Approval + var articleApproval *entity.ArticleApprovals + + if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == true { + articleApproval = &entity.ArticleApprovals{ + ArticleId: saveArticleRes.ID, + ApprovalBy: *newReq.CreatedById, + StatusId: statusIdOne, + Message: "Need Approval", + ApprovalAtLevel: &userLevelNumber, + } + } else { + articleApproval = &entity.ArticleApprovals{ + ArticleId: saveArticleRes.ID, + ApprovalBy: *newReq.CreatedById, + StatusId: statusIdTwo, + Message: "Publish Otomatis", + ApprovalAtLevel: nil, + } + } + + _, err = _i.ArticleApprovalsRepo.Create(articleApproval) + if err != nil { + return nil, err + } + + var categoryIds []string + if req.CategoryIds != "" { + categoryIds = strings.Split(req.CategoryIds, ",") + } + + _i.Log.Info().Interface("categoryIds", categoryIds).Msg("") + + for _, categoryId := range categoryIds { + categoryIdInt, _ := strconv.Atoi(categoryId) + + _i.Log.Info().Interface("categoryIdUint", uint(categoryIdInt)).Msg("") + + findCategory, err := _i.ArticleCategoriesRepo.FindOne(uint(categoryIdInt)) + + _i.Log.Info().Interface("findCategory", findCategory).Msg("") + + if err != nil { + return nil, err + } + + if findCategory == nil { + return nil, errors.New("category not found") + } + + categoryReq := articleCategoryDetailsReq.ArticleCategoryDetailsCreateRequest{ + ArticleId: saveArticleRes.ID, + CategoryId: categoryIdInt, + IsActive: true, + } + newCategoryReq := categoryReq.ToEntity() + + err = _i.ArticleCategoryDetailsRepo.Create(newCategoryReq) + if err != nil { + return nil, err + } + } + + return saveArticleRes, nil +} + +func (_i *articlesService) SaveThumbnail(c *fiber.Ctx) (err error) { + + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:articlesService", "Methods:SaveThumbnail"). + Interface("id", id).Msg("") + + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + if err != nil { + return err + } + files := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + // Iterasi semua file yang diunggah + for _, file := range files { + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop1"). + Interface("data", file).Msg("") + + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(file.Filename) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(file.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + objectName := fmt.Sprintf("articles/thumbnail/%d/%d/%s", now.Year(), now.Month(), newFilename) + + findCategory, err := _i.Repo.FindOne(uint(id)) + findCategory.ThumbnailName = &newFilename + findCategory.ThumbnailPath = &objectName + err = _i.Repo.Update(uint(id), findCategory) + if err != nil { + return err + } + + // Upload file ke MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, file.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + } + + return +} + +func (_i *articlesService) Update(id uint, req request.ArticlesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + if req.CreatedAt != nil { + layout := "2006-01-02 15:04:05" + parsedTime, err := time.Parse(layout, *req.CreatedAt) + if err != nil { + return fmt.Errorf("Error parsing time:", err) + } + newReq.CreatedAt = parsedTime + } + + return _i.Repo.UpdateSkipNull(id, newReq) +} + +func (_i *articlesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} + +func (_i *articlesService) Viewer(c *fiber.Ctx) (err error) { + thumbnailName := c.Params("thumbnailName") + + emptyImage := "empty-image.jpg" + searchThumbnail := emptyImage + if thumbnailName != emptyImage { + result, err := _i.Repo.FindByFilename(thumbnailName) + if err != nil { + return err + } + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:Viewer"). + Interface("resultThumbnail", result.ThumbnailPath).Msg("") + + if result.ThumbnailPath != nil { + searchThumbnail = *result.ThumbnailPath + } else { + searchThumbnail = "articles/thumbnail/" + emptyImage + } + } else { + searchThumbnail = "articles/thumbnail/" + emptyImage + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:Viewer"). + Interface("searchThumbnail", searchThumbnail).Msg("") + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := searchThumbnail + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + log.Fatalln(err) + } + defer fileContent.Close() + + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) + if contentType == "" { + contentType = "application/octet-stream" + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return +} + +func (_i *articlesService) UpdateActivityCount(id uint, activityTypeId int) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + viewCount := 0 + if result.ViewCount != nil { + viewCount = *result.ViewCount + } + shareCount := 0 + if result.ShareCount != nil { + shareCount = *result.ShareCount + } + commentCount := 0 + if result.CommentCount != nil { + commentCount = *result.CommentCount + } + + if activityTypeId == 2 { + viewCount++ + } else if activityTypeId == 3 { + shareCount++ + } else if activityTypeId == 4 { + commentCount++ + } + result.ViewCount = &viewCount + result.ShareCount = &shareCount + result.CommentCount = &commentCount + return _i.Repo.Update(id, result) +} + +func (_i *articlesService) SummaryStats(authToken string) (summaryStats *response.ArticleSummaryStats, err error) { + user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + result, err := _i.Repo.SummaryStats(user.ID) + if err != nil { + return nil, err + } + return result, nil +} + +func (_i *articlesService) ArticlePerUserLevelStats(authToken string, startDate *string, endDate *string) (articlePerUserLevelStats []*response.ArticlePerUserLevelStats, err error) { + user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("startDate", startDate).Msg("") + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("endDate", endDate).Msg("") + + var userLevelId *uint + var userLevelNumber *int + + if user != nil { + userLevelId = &user.UserLevelId + userLevelNumber = &user.UserLevel.LevelNumber + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("userLevelId", userLevelId).Msg("") + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "articlesService:ArticlePerUserLevelStats"). + Interface("userLevelNumber", userLevelNumber).Msg("") + + result, err := _i.Repo.ArticlePerUserLevelStats(userLevelId, userLevelNumber, nil, nil) + if err != nil { + return nil, err + } + return result, nil +} + +func (_i *articlesService) ArticleMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error) { + user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + var userLevelId *uint + var userLevelNumber *int + + if user != nil { + userLevelId = &user.UserLevelId + userLevelNumber = &user.UserLevel.LevelNumber + } + + result, err := _i.Repo.ArticleMonthlyStats(userLevelId, userLevelNumber, *year) + if err != nil { + return nil, err + } + return result, nil +} + +func (_i *articlesService) UpdateApproval(id uint, statusId int, userLevelId int, userLevelNumber int, userParentLevelId int) (err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + _i.Log.Info().Interface("statusId", statusId).Msg("") + + statusIdOne := 1 + statusIdTwo := 2 + statusIdThree := 3 + isPublish := true + isDraftFalse := false + + if statusId == 2 { + if userLevelNumber == 2 || userLevelNumber == 3 { + result.NeedApprovalFrom = &userParentLevelId + result.StatusId = &statusIdOne + } else { + result.NeedApprovalFrom = nil + result.StatusId = &statusIdTwo + + result.IsPublish = &isPublish + publishedAt := time.Now() + result.PublishedAt = &publishedAt + + result.IsDraft = &isDraftFalse + result.DraftedAt = nil + } + + userLevelIdStr := strconv.Itoa(userLevelId) + if result.HasApprovedBy == nil { + result.HasApprovedBy = &userLevelIdStr + } else { + hasApprovedBySlice := strings.Split(*result.HasApprovedBy, ",") + hasApprovedBySlice = append(hasApprovedBySlice, userLevelIdStr) + hasApprovedByJoin := strings.Join(hasApprovedBySlice, ",") + result.HasApprovedBy = &hasApprovedByJoin + } + } else if statusId == 3 { + result.StatusId = &statusIdThree + result.NeedApprovalFrom = nil + result.HasApprovedBy = nil + } + + err = _i.Repo.Update(id, result) + if err != nil { + return err + } + + return +} + +func (_i *articlesService) PublishScheduling(id uint, publishSchedule string) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + result.PublishSchedule = &publishSchedule + return _i.Repo.Update(id, result) +} + +func (_i *articlesService) UpdateBanner(id uint, isBanner bool) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + result.IsBanner = &isBanner + return _i.Repo.Update(id, result) +} + +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // jika tidak ada ekstensi, kembalikan string kosong + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // ambil ekstensi terakhir + return parts[len(parts)-1] +} diff --git a/app/module/cities/cities.module.go b/app/module/cities/cities.module.go new file mode 100644 index 0000000..6250600 --- /dev/null +++ b/app/module/cities/cities.module.go @@ -0,0 +1,54 @@ +package cities + +import ( + "narasi-ahli-be/app/module/cities/controller" + "narasi-ahli-be/app/module/cities/repository" + "narasi-ahli-be/app/module/cities/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of CitiesRouter +type CitiesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Cities module +var NewCitiesModule = fx.Options( + // register repository of Cities module + fx.Provide(repository.NewCitiesRepository), + + // register service of Cities module + fx.Provide(service.NewCitiesService), + + // register controller of Cities module + fx.Provide(controller.NewController), + + // register router of Cities module + fx.Provide(NewCitiesRouter), +) + +// init CitiesRouter +func NewCitiesRouter(fiber *fiber.App, controller *controller.Controller) *CitiesRouter { + return &CitiesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Cities module +func (_i *CitiesRouter) RegisterCitiesRoutes() { + // define controllers + citiesController := _i.Controller.Cities + + // define routes + _i.App.Route("/cities", func(router fiber.Router) { + router.Get("/", citiesController.All) + router.Get("/:id", citiesController.Show) + router.Post("/", citiesController.Save) + router.Put("/:id", citiesController.Update) + router.Delete("/:id", citiesController.Delete) + }) +} diff --git a/app/module/cities/controller/cities.controller.go b/app/module/cities/controller/cities.controller.go new file mode 100644 index 0000000..12cd762 --- /dev/null +++ b/app/module/cities/controller/cities.controller.go @@ -0,0 +1,185 @@ +package controller + +import ( + "narasi-ahli-be/app/module/cities/request" + "narasi-ahli-be/app/module/cities/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type citiesController struct { + citiesService service.CitiesService +} + +type CitiesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewCitiesController(citiesService service.CitiesService) CitiesController { + return &citiesController{ + citiesService: citiesService, + } +} + +// All Cities +// @Summary Get all Cities +// @Description API for getting all Cities +// @Tags Untags +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /cities [get] +func (_i *citiesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + var req request.CitiesQueryRequest + req.Pagination = paginate + + citiesData, paging, err := _i.citiesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Cities list successfully retrieved"}, + Data: citiesData, + Meta: paging, + }) +} + +// Show Cities +// @Summary Get one Cities +// @Description API for getting one Cities +// @Tags Untags +// @Security Bearer +// @Param id path int true "Cities ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /cities/{id} [get] +func (_i *citiesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + citiesData, err := _i.citiesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Cities successfully retrieved"}, + Data: citiesData, + }) +} + +// Save Cities +// @Summary Create Cities +// @Description API for create Cities +// @Tags Untags +// @Security Bearer +// @Param payload body request.CitiesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /cities [post] +func (_i *citiesController) Save(c *fiber.Ctx) error { + req := new(request.CitiesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.citiesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Cities successfully created"}, + }) +} + +// Update Cities +// @Summary Update Cities +// @Description API for update Cities +// @Tags Untags +// @Security Bearer +// @Body request.CitiesUpdateRequest +// @Param id path int true "Cities ID" +// @Param payload body request.CitiesUpdateRequest true "Required payload" +// @Accept json +// @Produce json +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /cities/{id} [put] +func (_i *citiesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.CitiesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.citiesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Cities successfully updated"}, + }) +} + +// Delete Cities +// @Summary Delete Cities +// @Description API for delete Cities +// @Tags Untags +// @Security Bearer +// @Param id path int true "Cities ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /cities/{id} [delete] +func (_i *citiesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.citiesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Cities successfully deleted"}, + }) +} diff --git a/app/module/cities/controller/controller.go b/app/module/cities/controller/controller.go new file mode 100644 index 0000000..76613b3 --- /dev/null +++ b/app/module/cities/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/cities/service" + +type Controller struct { + Cities CitiesController +} + +func NewController(CitiesService service.CitiesService) *Controller { + return &Controller{ + Cities: NewCitiesController(CitiesService), + } +} diff --git a/app/module/cities/mapper/cities.mapper.go b/app/module/cities/mapper/cities.mapper.go new file mode 100644 index 0000000..294cda4 --- /dev/null +++ b/app/module/cities/mapper/cities.mapper.go @@ -0,0 +1,17 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/cities/response" +) + +func CitiesResponseMapper(citiesReq *entity.Cities) (citiesRes *res.CitiesResponse) { + if citiesReq != nil { + citiesRes = &res.CitiesResponse{ + ID: citiesReq.ID, + CityName: citiesReq.CityName, + ProvId: citiesReq.ProvId, + } + } + return citiesRes +} diff --git a/app/module/cities/repository/cities.repository.go b/app/module/cities/repository/cities.repository.go new file mode 100644 index 0000000..d5a057f --- /dev/null +++ b/app/module/cities/repository/cities.repository.go @@ -0,0 +1,69 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/cities/request" + "narasi-ahli-be/utils/paginator" +) + +type citiesRepository struct { + DB *database.Database +} + +// CitiesRepository define interface of ICitiesRepository +type CitiesRepository interface { + GetAll(req request.CitiesQueryRequest) (citiess []*entity.Cities, paging paginator.Pagination, err error) + FindOne(id uint) (cities *entity.Cities, err error) + Create(cities *entity.Cities) (err error) + Update(id uint, cities *entity.Cities) (err error) + Delete(id uint) (err error) +} + +func NewCitiesRepository(db *database.Database) CitiesRepository { + return &citiesRepository{ + DB: db, + } +} + +// implement interface of ICitiesRepository +func (_i *citiesRepository) GetAll(req request.CitiesQueryRequest) (citiess []*entity.Cities, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Cities{}) + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&citiess).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *citiesRepository) FindOne(id uint) (cities *entity.Cities, err error) { + if err := _i.DB.DB.First(&cities, id).Error; err != nil { + return nil, err + } + + return cities, nil +} + +func (_i *citiesRepository) Create(cities *entity.Cities) (err error) { + return _i.DB.DB.Create(cities).Error +} + +func (_i *citiesRepository) Update(id uint, cities *entity.Cities) (err error) { + return _i.DB.DB.Model(&entity.Cities{}). + Where(&entity.Cities{ID: id}). + Updates(cities).Error +} + +func (_i *citiesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.Cities{}, id).Error +} diff --git a/app/module/cities/request/cities.request.go b/app/module/cities/request/cities.request.go new file mode 100644 index 0000000..6bbeeaf --- /dev/null +++ b/app/module/cities/request/cities.request.go @@ -0,0 +1,42 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" +) + +type CitiesGeneric interface { + ToEntity() +} + +type CitiesQueryRequest struct { + CityName string `json:"city_name" validate:"required"` + ProvId int `json:"prov_id" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type CitiesCreateRequest struct { + CityName string `json:"city_name" validate:"required"` + ProvId int `json:"prov_id" validate:"required"` +} + +func (req CitiesCreateRequest) ToEntity() *entity.Cities { + return &entity.Cities{ + CityName: req.CityName, + ProvId: req.ProvId, + } +} + +type CitiesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + CityName string `json:"city_name" validate:"required"` + ProvId int `json:"prov_id" validate:"required"` +} + +func (req CitiesUpdateRequest) ToEntity() *entity.Cities { + return &entity.Cities{ + ID: req.ID, + CityName: req.CityName, + ProvId: req.ProvId, + } +} diff --git a/app/module/cities/response/cities.response.go b/app/module/cities/response/cities.response.go new file mode 100644 index 0000000..8941551 --- /dev/null +++ b/app/module/cities/response/cities.response.go @@ -0,0 +1,7 @@ +package response + +type CitiesResponse struct { + ID uint `json:"id"` + CityName string `json:"city_name"` + ProvId int `json:"prov_id"` +} diff --git a/app/module/cities/service/cities.service.go b/app/module/cities/service/cities.service.go new file mode 100644 index 0000000..74f6f58 --- /dev/null +++ b/app/module/cities/service/cities.service.go @@ -0,0 +1,73 @@ +package service + +import ( + "narasi-ahli-be/app/module/cities/mapper" + "narasi-ahli-be/app/module/cities/repository" + "narasi-ahli-be/app/module/cities/request" + "narasi-ahli-be/app/module/cities/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// CitiesService +type citiesService struct { + Repo repository.CitiesRepository + Log zerolog.Logger +} + +// CitiesService define interface of ICitiesService +type CitiesService interface { + All(req request.CitiesQueryRequest) (cities []*response.CitiesResponse, paging paginator.Pagination, err error) + Show(id uint) (cities *response.CitiesResponse, err error) + Save(req request.CitiesCreateRequest) (err error) + Update(id uint, req request.CitiesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewCitiesService init CitiesService +func NewCitiesService(repo repository.CitiesRepository, log zerolog.Logger) CitiesService { + + return &citiesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of CitiesService +func (_i *citiesService) All(req request.CitiesQueryRequest) (citiess []*response.CitiesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + citiess = append(citiess, mapper.CitiesResponseMapper(result)) + } + + return +} + +func (_i *citiesService) Show(id uint) (cities *response.CitiesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.CitiesResponseMapper(result), nil +} + +func (_i *citiesService) Save(req request.CitiesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *citiesService) Update(id uint, req request.CitiesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *citiesService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/communications/mapper/communications.mapper.go b/app/module/communications/mapper/communications.mapper.go new file mode 100644 index 0000000..c44c2f7 --- /dev/null +++ b/app/module/communications/mapper/communications.mapper.go @@ -0,0 +1,78 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/communications/response" +) + +func ConversationsResponseMapper(conversation *entity.Conversations) *response.ConversationsResponse { + result := &response.ConversationsResponse{ + ID: conversation.ID, + Participant1ID: conversation.Participant1ID, + Participant2ID: conversation.Participant2ID, + LastMessageAt: conversation.LastMessageAt, + CreatedAt: conversation.CreatedAt, + UpdatedAt: conversation.UpdatedAt, + } + + if conversation.Participant1 != nil { + result.Participant1 = &response.UserBasicInfo{ + ID: conversation.Participant1.ID, + Username: conversation.Participant1.Username, + Fullname: conversation.Participant1.Fullname, + Email: conversation.Participant1.Email, + } + } + + if conversation.Participant2 != nil { + result.Participant2 = &response.UserBasicInfo{ + ID: conversation.Participant2.ID, + Username: conversation.Participant2.Username, + Fullname: conversation.Participant2.Fullname, + Email: conversation.Participant2.Email, + } + } + + return result +} + +func ChatMessagesResponseMapper(chatMessage *entity.ChatMessages) *response.ChatMessagesResponse { + result := &response.ChatMessagesResponse{ + ID: chatMessage.ID, + ConversationID: chatMessage.ConversationID, + SenderID: chatMessage.SenderID, + MessageText: chatMessage.MessageText, + MessageType: chatMessage.MessageType, + FileURL: chatMessage.FileURL, + FileName: chatMessage.FileName, + FileSize: chatMessage.FileSize, + IsRead: chatMessage.IsRead, + CreatedAt: chatMessage.CreatedAt, + } + + if chatMessage.Sender != nil { + result.Sender = &response.UserBasicInfo{ + ID: chatMessage.Sender.ID, + Username: chatMessage.Sender.Username, + Fullname: chatMessage.Sender.Fullname, + Email: chatMessage.Sender.Email, + } + } + + return result +} + +func ConversationWithMessagesResponseMapper(conversation *entity.Conversations, messages []*entity.ChatMessages, unreadCount int) *response.ConversationWithMessagesResponse { + conversationResponse := ConversationsResponseMapper(conversation) + conversationResponse.UnreadCount = unreadCount + + var messagesResponse []*response.ChatMessagesResponse + for _, message := range messages { + messagesResponse = append(messagesResponse, ChatMessagesResponseMapper(message)) + } + + return &response.ConversationWithMessagesResponse{ + Conversation: conversationResponse, + Messages: messagesResponse, + } +} diff --git a/app/module/communications/repository/communications.repository.go b/app/module/communications/repository/communications.repository.go new file mode 100644 index 0000000..0161058 --- /dev/null +++ b/app/module/communications/repository/communications.repository.go @@ -0,0 +1,130 @@ +package repository + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/communications/request" + "narasi-ahli-be/utils/paginator" + "time" + + "gorm.io/gorm" +) + +type communicationsRepository struct { + DB *gorm.DB +} + +type CommunicationsRepository interface { + // Conversations + GetUserConversations(userId uint, req request.ConversationsQueryRequest) (conversations []*entity.Conversations, paging paginator.Pagination, err error) + FindConversationByParticipants(participant1ID, participant2ID uint) (conversation *entity.Conversations, err error) + FindConversationByUserAndId(userId uint, conversationId uint) (conversation *entity.Conversations, err error) + CreateConversation(conversation *entity.Conversations) (result *entity.Conversations, err error) + UpdateConversationLastMessage(conversationId uint, lastMessageAt *time.Time) (err error) + + // Chat Messages + GetConversationMessages(conversationId uint, req request.ChatMessagesQueryRequest) (messages []*entity.ChatMessages, paging paginator.Pagination, err error) + CreateChatMessage(message *entity.ChatMessages) (result *entity.ChatMessages, err error) + MarkMessageAsRead(messageId uint, userId uint) (err error) + GetUnreadCount(conversationId uint, userId uint) (count int64, err error) +} + +func NewCommunicationsRepository(db *gorm.DB) CommunicationsRepository { + return &communicationsRepository{ + DB: db, + } +} + +// Conversations methods +func (_i *communicationsRepository) GetUserConversations(userId uint, req request.ConversationsQueryRequest) (conversations []*entity.Conversations, paging paginator.Pagination, err error) { + query := _i.DB.Where("participant1_id = ? OR participant2_id = ?", userId, userId) + + // Include relationships + query = query.Preload("Participant1").Preload("Participant2") + + // Order by last message time desc, then by created_at desc + query = query.Order("last_message_at DESC NULLS LAST, created_at DESC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&conversations).Error + paging = *req.Pagination + + return +} + +func (_i *communicationsRepository) FindConversationByParticipants(participant1ID, participant2ID uint) (conversation *entity.Conversations, err error) { + err = _i.DB.Where( + "(participant1_id = ? AND participant2_id = ?) OR (participant1_id = ? AND participant2_id = ?)", + participant1ID, participant2ID, participant2ID, participant1ID).Preload("Participant1").Preload("Participant2").First(&conversation).Error + return +} + +func (_i *communicationsRepository) FindConversationByUserAndId(userId uint, conversationId uint) (conversation *entity.Conversations, err error) { + err = _i.DB.Where( + "id = ? AND (participant1_id = ? OR participant2_id = ?)", + conversationId, userId, userId).Preload("Participant1").Preload("Participant2").First(&conversation).Error + return +} + +func (_i *communicationsRepository) CreateConversation(conversation *entity.Conversations) (result *entity.Conversations, err error) { + err = _i.DB.Create(conversation).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.Preload("Participant1").Preload("Participant2").First(&result, conversation.ID).Error + return +} + +func (_i *communicationsRepository) UpdateConversationLastMessage(conversationId uint, lastMessageAt *time.Time) (err error) { + err = _i.DB.Model(&entity.Conversations{}).Where("id = ?", conversationId).Update("last_message_at", lastMessageAt).Error + return +} + +// Chat Messages methods +func (_i *communicationsRepository) GetConversationMessages(conversationId uint, req request.ChatMessagesQueryRequest) (messages []*entity.ChatMessages, paging paginator.Pagination, err error) { + query := _i.DB.Where("conversation_id = ?", conversationId) + + // Include sender relationship + query = query.Preload("Sender") + + // Order by created_at asc (oldest first for chat) + query = query.Order("created_at ASC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&messages).Error + paging = *req.Pagination + + return +} + +func (_i *communicationsRepository) CreateChatMessage(message *entity.ChatMessages) (result *entity.ChatMessages, err error) { + err = _i.DB.Create(message).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.Preload("Sender").First(&result, message.ID).Error + return +} + +func (_i *communicationsRepository) MarkMessageAsRead(messageId uint, userId uint) (err error) { + err = _i.DB.Model(&entity.ChatMessages{}).Where("id = ? AND sender_id != ?", messageId, userId).Update("is_read", true).Error + return +} + +func (_i *communicationsRepository) GetUnreadCount(conversationId uint, userId uint) (count int64, err error) { + err = _i.DB.Model(&entity.ChatMessages{}).Where("conversation_id = ? AND sender_id != ? AND is_read = false", conversationId, userId).Count(&count).Error + return +} diff --git a/app/module/communications/request/communications.request.go b/app/module/communications/request/communications.request.go new file mode 100644 index 0000000..952e742 --- /dev/null +++ b/app/module/communications/request/communications.request.go @@ -0,0 +1,90 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" +) + +// Conversations Request DTOs +type ConversationsQueryRequest struct { + Pagination *paginator.Pagination `json:"pagination"` +} + +type ConversationsCreateRequest struct { + Participant2ID uint `json:"participant2Id" validate:"required"` +} + +func (req ConversationsCreateRequest) ToEntity() *entity.Conversations { + return &entity.Conversations{ + Participant1ID: 0, // Will be set in service layer + Participant2ID: req.Participant2ID, + } +} + +// Chat Messages Request DTOs +type ChatMessagesQueryRequest struct { + ConversationID uint `json:"conversationId" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ChatMessagesCreateRequest struct { + ConversationID uint `json:"conversationId" validate:"required"` + MessageText string `json:"messageText"` + MessageType string `json:"messageType" validate:"required,oneof=text image file audio"` +} + +func (req ChatMessagesCreateRequest) ToEntity() *entity.ChatMessages { + return &entity.ChatMessages{ + ConversationID: req.ConversationID, + SenderID: 0, // Will be set in service layer + MessageText: &req.MessageText, + MessageType: req.MessageType, + IsRead: false, + } +} + +type ChatMessagesFileUploadRequest struct { + ConversationID uint `json:"conversationId" validate:"required"` + MessageType string `json:"messageType" validate:"required,oneof=image file audio"` + FileName string `json:"fileName"` + FileSize int64 `json:"fileSize"` +} + +func (req ChatMessagesFileUploadRequest) ToEntity(fileURL string) *entity.ChatMessages { + return &entity.ChatMessages{ + ConversationID: req.ConversationID, + SenderID: 0, // Will be set in service layer + MessageType: req.MessageType, + FileURL: &fileURL, + FileName: &req.FileName, + FileSize: &req.FileSize, + IsRead: false, + } +} + +type ChatMessagesMarkReadRequest struct { + MessageID uint `json:"messageId" validate:"required"` +} + +type ConversationsQueryRequestContext struct { + // No specific query parameters for conversations list +} + +func (req ConversationsQueryRequestContext) ToParamRequest() ConversationsQueryRequest { + return ConversationsQueryRequest{} +} + +type ChatMessagesQueryRequestContext struct { + ConversationID string `json:"conversationId"` +} + +func (req ChatMessagesQueryRequestContext) ToParamRequest() ChatMessagesQueryRequest { + var request ChatMessagesQueryRequest + + if conversationIDStr := req.ConversationID; conversationIDStr != "" { + // Parse conversation ID from string to uint + // This will be handled in the controller + } + + return request +} diff --git a/app/module/communications/response/communications.response.go b/app/module/communications/response/communications.response.go new file mode 100644 index 0000000..4bdeee6 --- /dev/null +++ b/app/module/communications/response/communications.response.go @@ -0,0 +1,56 @@ +package response + +import ( + "time" +) + +// Conversations Response DTOs +type ConversationsResponse struct { + ID uint `json:"id"` + Participant1ID uint `json:"participant1Id"` + Participant2ID uint `json:"participant2Id"` + LastMessageAt *time.Time `json:"lastMessageAt"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + // Nested user info + Participant1 *UserBasicInfo `json:"participant1,omitempty"` + Participant2 *UserBasicInfo `json:"participant2,omitempty"` + + // Last message preview + LastMessage *ChatMessagesResponse `json:"lastMessage,omitempty"` + + // Unread count + UnreadCount int `json:"unreadCount"` +} + +// Chat Messages Response DTOs +type ChatMessagesResponse struct { + ID uint `json:"id"` + ConversationID uint `json:"conversationId"` + SenderID uint `json:"senderId"` + MessageText *string `json:"messageText"` + MessageType string `json:"messageType"` + FileURL *string `json:"fileUrl"` + FileName *string `json:"fileName"` + FileSize *int64 `json:"fileSize"` + IsRead bool `json:"isRead"` + CreatedAt time.Time `json:"createdAt"` + + // Nested user info + Sender *UserBasicInfo `json:"sender,omitempty"` +} + +type UserBasicInfo struct { + ID uint `json:"id"` + Username string `json:"username"` + Fullname string `json:"fullname"` + Email string `json:"email"` +} + +// Conversation with messages +type ConversationWithMessagesResponse struct { + Conversation *ConversationsResponse `json:"conversation"` + Messages []*ChatMessagesResponse `json:"messages"` + Pagination interface{} `json:"pagination"` +} diff --git a/app/module/custom_static_pages/controller/controller.go b/app/module/custom_static_pages/controller/controller.go new file mode 100644 index 0000000..b0b1239 --- /dev/null +++ b/app/module/custom_static_pages/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/custom_static_pages/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + CustomStaticPages CustomStaticPagesController +} + +func NewController(CustomStaticPagesService service.CustomStaticPagesService, log zerolog.Logger) *Controller { + return &Controller{ + CustomStaticPages: NewCustomStaticPagesController(CustomStaticPagesService, log), + } +} diff --git a/app/module/custom_static_pages/controller/custom_static_pages.controller.go b/app/module/custom_static_pages/controller/custom_static_pages.controller.go new file mode 100644 index 0000000..e11edde --- /dev/null +++ b/app/module/custom_static_pages/controller/custom_static_pages.controller.go @@ -0,0 +1,237 @@ +package controller + +import ( + "narasi-ahli-be/app/module/custom_static_pages/request" + "narasi-ahli-be/app/module/custom_static_pages/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type customStaticPagesController struct { + customStaticPagesService service.CustomStaticPagesService + Log zerolog.Logger +} + +type CustomStaticPagesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + ShowBySlug(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewCustomStaticPagesController(customStaticPagesService service.CustomStaticPagesService, log zerolog.Logger) CustomStaticPagesController { + return &customStaticPagesController{ + customStaticPagesService: customStaticPagesService, + Log: log, + } +} + +// All get all CustomStaticPages +// @Summary Get all CustomStaticPages +// @Description API for getting all CustomStaticPages +// @Tags CustomStaticPages +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.CustomStaticPagesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /custom-static-pages [get] +func (_i *customStaticPagesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.CustomStaticPagesQueryRequestContext{ + Title: c.Query("title"), + Description: c.Query("description"), + Slug: c.Query("slug"), + HtmlBody: c.Query("htmlBody"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + // Get from context + customStaticPagesData, paging, err := _i.customStaticPagesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"CustomStaticPages list successfully retrieved"}, + Data: customStaticPagesData, + Meta: paging, + }) +} + +// Show get one CustomStaticPages +// @Summary Get one CustomStaticPages +// @Description API for getting one CustomStaticPages +// @Tags CustomStaticPages +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "CustomStaticPages ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /custom-static-pages/{id} [get] +func (_i *customStaticPagesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + // Get from context + customStaticPagesData, err := _i.customStaticPagesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"CustomStaticPages successfully retrieved"}, + Data: customStaticPagesData, + }) +} + +// ShowBySlug get one CustomStaticPages +// @Summary Get one CustomStaticPages +// @Description API for getting one CustomStaticPages +// @Tags CustomStaticPages +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param slug path string true "CustomStaticPages Slug" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /custom-static-pages/slug/{slug} [get] +func (_i *customStaticPagesController) ShowBySlug(c *fiber.Ctx) error { + slug := c.Params("slug") + + // Get from context + customStaticPagesData, err := _i.customStaticPagesService.ShowBySlug(slug) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"CustomStaticPages successfully retrieved"}, + Data: customStaticPagesData, + }) +} + +// Save create CustomStaticPages +// @Summary Create CustomStaticPages +// @Description API for create CustomStaticPages +// @Tags CustomStaticPages +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.CustomStaticPagesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /custom-static-pages [post] +func (_i *customStaticPagesController) Save(c *fiber.Ctx) error { + req := new(request.CustomStaticPagesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + // Get from context + authToken := c.Get("Authorization") + dataResult, err := _i.customStaticPagesService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"CustomStaticPages successfully created"}, + Data: dataResult, + }) +} + +// Update update CustomStaticPages +// @Summary update CustomStaticPages +// @Description API for update CustomStaticPages +// @Tags CustomStaticPages +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.CustomStaticPagesUpdateRequest true "Required payload" +// @Param id path int true "CustomStaticPages ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /custom-static-pages/{id} [put] +func (_i *customStaticPagesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.CustomStaticPagesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + // Get from context + err = _i.customStaticPagesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"CustomStaticPages successfully updated"}, + }) +} + +// Delete delete CustomStaticPages +// @Summary delete CustomStaticPages +// @Description API for delete CustomStaticPages +// @Tags CustomStaticPages +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "CustomStaticPages ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /custom-static-pages/{id} [delete] +func (_i *customStaticPagesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + // Get from context + err = _i.customStaticPagesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"CustomStaticPages successfully deleted"}, + }) +} diff --git a/app/module/custom_static_pages/custom_static_pages.module.go b/app/module/custom_static_pages/custom_static_pages.module.go new file mode 100644 index 0000000..40db166 --- /dev/null +++ b/app/module/custom_static_pages/custom_static_pages.module.go @@ -0,0 +1,55 @@ +package custom_static_pages + +import ( + "narasi-ahli-be/app/module/custom_static_pages/controller" + "narasi-ahli-be/app/module/custom_static_pages/repository" + "narasi-ahli-be/app/module/custom_static_pages/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of CustomStaticPagesRouter +type CustomStaticPagesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of CustomStaticPages module +var NewCustomStaticPagesModule = fx.Options( + // register repository of CustomStaticPages module + fx.Provide(repository.NewCustomStaticPagesRepository), + + // register service of CustomStaticPages module + fx.Provide(service.NewCustomStaticPagesService), + + // register controller of CustomStaticPages module + fx.Provide(controller.NewController), + + // register router of CustomStaticPages module + fx.Provide(NewCustomStaticPagesRouter), +) + +// init CustomStaticPagesRouter +func NewCustomStaticPagesRouter(fiber *fiber.App, controller *controller.Controller) *CustomStaticPagesRouter { + return &CustomStaticPagesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of CustomStaticPages module +func (_i *CustomStaticPagesRouter) RegisterCustomStaticPagesRoutes() { + // define controllers + customStaticPagesController := _i.Controller.CustomStaticPages + + // define routes + _i.App.Route("/custom-static-pages", func(router fiber.Router) { + router.Get("/", customStaticPagesController.All) + router.Get("/:id", customStaticPagesController.Show) + router.Get("/slug/:slug", customStaticPagesController.ShowBySlug) + router.Post("/", customStaticPagesController.Save) + router.Put("/:id", customStaticPagesController.Update) + router.Delete("/:id", customStaticPagesController.Delete) + }) +} diff --git a/app/module/custom_static_pages/mapper/custom_static_pages.mapper.go b/app/module/custom_static_pages/mapper/custom_static_pages.mapper.go new file mode 100644 index 0000000..ba188cc --- /dev/null +++ b/app/module/custom_static_pages/mapper/custom_static_pages.mapper.go @@ -0,0 +1,22 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/custom_static_pages/response" +) + +func CustomStaticPagesResponseMapper(customStaticPagesReq *entity.CustomStaticPages) (customStaticPagesRes *res.CustomStaticPagesResponse) { + if customStaticPagesReq != nil { + customStaticPagesRes = &res.CustomStaticPagesResponse{ + ID: customStaticPagesReq.ID, + Title: customStaticPagesReq.Title, + Description: customStaticPagesReq.Description, + Slug: customStaticPagesReq.Slug, + HtmlBody: customStaticPagesReq.HtmlBody, + IsActive: customStaticPagesReq.IsActive, + CreatedAt: customStaticPagesReq.CreatedAt, + UpdatedAt: customStaticPagesReq.UpdatedAt, + } + } + return customStaticPagesRes +} diff --git a/app/module/custom_static_pages/repository/custom_static_pages.repository.go b/app/module/custom_static_pages/repository/custom_static_pages.repository.go new file mode 100644 index 0000000..23f8377 --- /dev/null +++ b/app/module/custom_static_pages/repository/custom_static_pages.repository.go @@ -0,0 +1,113 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/custom_static_pages/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + + "github.com/rs/zerolog" +) + +type customStaticPagesRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// CustomStaticPagesRepository define interface of ICustomStaticPagesRepository +type CustomStaticPagesRepository interface { + GetAll(req request.CustomStaticPagesQueryRequest) (customStaticPagess []*entity.CustomStaticPages, paging paginator.Pagination, err error) + FindOne(id uint) (customStaticPages *entity.CustomStaticPages, err error) + FindOneBySlug(slug string) (customStaticPages *entity.CustomStaticPages, err error) + Create(customStaticPages *entity.CustomStaticPages) (customStaticPagesReturn *entity.CustomStaticPages, err error) + Update(id uint, customStaticPages *entity.CustomStaticPages) (err error) + Delete(id uint) (err error) +} + +func NewCustomStaticPagesRepository(db *database.Database, logger zerolog.Logger) CustomStaticPagesRepository { + return &customStaticPagesRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of ICustomStaticPagesRepository +func (_i *customStaticPagesRepository) GetAll(req request.CustomStaticPagesQueryRequest) (customStaticPagess []*entity.CustomStaticPages, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.CustomStaticPages{}) + query = query.Where("is_active = ?", true) + query = query.Where("client_id = ?") + + if req.Title != nil && *req.Title != "" { + title := strings.ToLower(*req.Title) + query = query.Where("LOWER(title) LIKE ?", "%"+strings.ToLower(title)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.Slug != nil && *req.Slug != "" { + slug := strings.ToLower(*req.Slug) + query = query.Where("LOWER(slug) LIKE ?", "%"+strings.ToLower(slug)+"%") + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&customStaticPagess).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *customStaticPagesRepository) FindOne(id uint) (customStaticPages *entity.CustomStaticPages, err error) { + if err := _i.DB.DB.Where("client_id = ?").First(&customStaticPages, id).Error; err != nil { + return nil, err + } + + return customStaticPages, nil +} + +func (_i *customStaticPagesRepository) FindOneBySlug(slug string) (customStaticPages *entity.CustomStaticPages, err error) { + if err := _i.DB.DB.Where("client_id = ? AND slug = ?", slug).First(&customStaticPages).Error; err != nil { + return nil, err + } + + return customStaticPages, nil +} + +func (_i *customStaticPagesRepository) Create(customStaticPages *entity.CustomStaticPages) (customStaticPagesReturn *entity.CustomStaticPages, err error) { + result := _i.DB.DB.Create(customStaticPages) + return customStaticPages, result.Error +} + +func (_i *customStaticPagesRepository) Update(id uint, customStaticPages *entity.CustomStaticPages) (err error) { + customStaticPagesMap, err := utilSvc.StructToMap(customStaticPages) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.CustomStaticPages{}). + Where(&entity.CustomStaticPages{ID: id}). + Updates(customStaticPagesMap).Error +} + +func (_i *customStaticPagesRepository) Delete(id uint) error { + return _i.DB.DB.Where("client_id = ?").Delete(&entity.CustomStaticPages{}, id).Error +} diff --git a/app/module/custom_static_pages/request/custom_static_pages.request.go b/app/module/custom_static_pages/request/custom_static_pages.request.go new file mode 100644 index 0000000..33e46d5 --- /dev/null +++ b/app/module/custom_static_pages/request/custom_static_pages.request.go @@ -0,0 +1,79 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "time" +) + +type CustomStaticPagesGeneric interface { + ToEntity() +} + +type CustomStaticPagesQueryRequest struct { + Title *string `json:"title"` + Description *string `json:"description"` + Slug *string `json:"slug"` + HtmlBody *string `json:"htmlBody"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type CustomStaticPagesCreateRequest struct { + Title string `json:"title" validate:"required"` + Description string `json:"description"` + Slug string `json:"slug" validate:"required"` + HtmlBody string `json:"htmlBody" validate:"required"` +} + +func (req CustomStaticPagesCreateRequest) ToEntity() *entity.CustomStaticPages { + return &entity.CustomStaticPages{ + Title: req.Title, + Description: req.Description, + Slug: req.Slug, + HtmlBody: req.HtmlBody, + IsActive: true, + } +} + +type CustomStaticPagesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description"` + Slug string `json:"slug" validate:"required"` + HtmlBody string `json:"htmlBody" validate:"required"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (req CustomStaticPagesUpdateRequest) ToEntity() *entity.CustomStaticPages { + return &entity.CustomStaticPages{ + ID: req.ID, + Title: req.Title, + Description: req.Description, + Slug: req.Slug, + HtmlBody: req.HtmlBody, + UpdatedAt: req.UpdatedAt, + } +} + +type CustomStaticPagesQueryRequestContext struct { + Title string `json:"title"` + Description string `json:"description"` + Slug string `json:"slug"` + HtmlBody string `json:"htmlBody"` +} + +func (req CustomStaticPagesQueryRequestContext) ToParamRequest() CustomStaticPagesQueryRequest { + var request CustomStaticPagesQueryRequest + + if title := req.Title; title != "" { + request.Title = &title + } + if description := req.Description; description != "" { + request.Description = &description + } + if slug := req.Slug; slug != "" { + request.Slug = &slug + } + + return request +} diff --git a/app/module/custom_static_pages/response/custom_static_pages.response.go b/app/module/custom_static_pages/response/custom_static_pages.response.go new file mode 100644 index 0000000..89161f9 --- /dev/null +++ b/app/module/custom_static_pages/response/custom_static_pages.response.go @@ -0,0 +1,14 @@ +package response + +import "time" + +type CustomStaticPagesResponse struct { + ID uint `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Slug string `json:"slug"` + HtmlBody string `json:"htmlBody"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/custom_static_pages/service/custom_static_pages.service.go b/app/module/custom_static_pages/service/custom_static_pages.service.go new file mode 100644 index 0000000..9b44588 --- /dev/null +++ b/app/module/custom_static_pages/service/custom_static_pages.service.go @@ -0,0 +1,98 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/custom_static_pages/mapper" + "narasi-ahli-be/app/module/custom_static_pages/repository" + "narasi-ahli-be/app/module/custom_static_pages/request" + "narasi-ahli-be/app/module/custom_static_pages/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// CustomStaticPagesService +type customStaticPagesService struct { + Repo repository.CustomStaticPagesRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +// CustomStaticPagesService define interface of ICustomStaticPagesService +type CustomStaticPagesService interface { + All(req request.CustomStaticPagesQueryRequest) (customStaticPages []*response.CustomStaticPagesResponse, paging paginator.Pagination, err error) + Show(id uint) (customStaticPages *response.CustomStaticPagesResponse, err error) + ShowBySlug(slug string) (customStaticPages *response.CustomStaticPagesResponse, err error) + Save(req request.CustomStaticPagesCreateRequest, authToken string) (customStaticPages *entity.CustomStaticPages, err error) + Update(id uint, req request.CustomStaticPagesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewCustomStaticPagesService init CustomStaticPagesService +func NewCustomStaticPagesService(repo repository.CustomStaticPagesRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) CustomStaticPagesService { + + return &customStaticPagesService{ + Repo: repo, + Log: log, + UsersRepo: usersRepo, + } +} + +// All implement interface of CustomStaticPagesService +func (_i *customStaticPagesService) All(req request.CustomStaticPagesQueryRequest) (customStaticPagess []*response.CustomStaticPagesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + customStaticPagess = append(customStaticPagess, mapper.CustomStaticPagesResponseMapper(result)) + } + + return +} + +func (_i *customStaticPagesService) Show(id uint) (customStaticPages *response.CustomStaticPagesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.CustomStaticPagesResponseMapper(result), nil +} + +func (_i *customStaticPagesService) ShowBySlug(slug string) (customStaticPages *response.CustomStaticPagesResponse, err error) { + result, err := _i.Repo.FindOneBySlug(slug) + if err != nil { + return nil, err + } + + return mapper.CustomStaticPagesResponseMapper(result), nil +} + +func (_i *customStaticPagesService) Save(req request.CustomStaticPagesCreateRequest, authToken string) (customStaticPages *entity.CustomStaticPages, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + newReq := req.ToEntity() + + //createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + //newReq.CreatedById = &createdBy.ID + + return _i.Repo.Create(newReq) +} + +func (_i *customStaticPagesService) Update(id uint, req request.CustomStaticPagesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *customStaticPagesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + result.IsActive = false + return _i.Repo.Update(id, result) +} diff --git a/app/module/districts/controller/controller.go b/app/module/districts/controller/controller.go new file mode 100644 index 0000000..5f0b182 --- /dev/null +++ b/app/module/districts/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/districts/service" + +type Controller struct { + Districts DistrictsController +} + +func NewController(DistrictsService service.DistrictsService) *Controller { + return &Controller{ + Districts: NewDistrictsController(DistrictsService), + } +} diff --git a/app/module/districts/controller/districts.controller.go b/app/module/districts/controller/districts.controller.go new file mode 100644 index 0000000..72bb1c4 --- /dev/null +++ b/app/module/districts/controller/districts.controller.go @@ -0,0 +1,182 @@ +package controller + +import ( + "narasi-ahli-be/app/module/districts/request" + "narasi-ahli-be/app/module/districts/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type districtsController struct { + districtsService service.DistrictsService +} + +type DistrictsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewDistrictsController(districtsService service.DistrictsService) DistrictsController { + return &districtsController{ + districtsService: districtsService, + } +} + +// All Districts +// @Summary Get all Districts +// @Description API for getting all Districts +// @Tags Untags +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /districts [get] +func (_i *districtsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + var req request.DistrictsQueryRequest + req.Pagination = paginate + + districtsData, paging, err := _i.districtsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Districts list successfully retrieved"}, + Data: districtsData, + Meta: paging, + }) +} + +// Show Districts +// @Summary Get one Districts +// @Description API for getting one Districts +// @Tags Untags +// @Security Bearer +// @Param id path int true "Districts ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /districts/{id} [get] +func (_i *districtsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + districtsData, err := _i.districtsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Districts successfully retrieved"}, + Data: districtsData, + }) +} + +// Save Districts +// @Summary Create Districts +// @Description API for create Districts +// @Tags Untags +// @Security Bearer +// @Body request.DistrictsCreateRequest +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /districts [post] +func (_i *districtsController) Save(c *fiber.Ctx) error { + req := new(request.DistrictsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.districtsService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Districts successfully created"}, + }) +} + +// Update Districts +// @Summary Update Districts +// @Description API for update Districts +// @Tags Untags +// @Security Bearer +// @Body request.DistrictsUpdateRequest +// @Param id path int true "Districts ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /districts/{id} [put] +func (_i *districtsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.DistrictsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.districtsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Districts successfully updated"}, + }) +} + +// Delete Districts +// @Summary Delete Districts +// @Description API for delete Districts +// @Tags Untags +// @Security Bearer +// @Param id path int true "Districts ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /districts/{id} [delete] +func (_i *districtsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.districtsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Districts successfully deleted"}, + }) +} diff --git a/app/module/districts/districts.module.go b/app/module/districts/districts.module.go new file mode 100644 index 0000000..0bbad21 --- /dev/null +++ b/app/module/districts/districts.module.go @@ -0,0 +1,54 @@ +package districts + +import ( + "narasi-ahli-be/app/module/districts/controller" + "narasi-ahli-be/app/module/districts/repository" + "narasi-ahli-be/app/module/districts/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// DistrictsRouter struct of DistrictsRouter +type DistrictsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// NewDistrictsModule register bulky of Districts module +var NewDistrictsModule = fx.Options( + // register repository of Districts module + fx.Provide(repository.NewDistrictsRepository), + + // register service of Districts module + fx.Provide(service.NewDistrictsService), + + // register controller of Districts module + fx.Provide(controller.NewController), + + // register router of Districts module + fx.Provide(NewDistrictsRouter), +) + +// NewDistrictsRouter init DistrictsRouter +func NewDistrictsRouter(fiber *fiber.App, controller *controller.Controller) *DistrictsRouter { + return &DistrictsRouter{ + App: fiber, + Controller: controller, + } +} + +// RegisterDistrictsRoutes register routes of Districts module +func (_i *DistrictsRouter) RegisterDistrictsRoutes() { + // define controllers + districtsController := _i.Controller.Districts + + // define routes + _i.App.Route("/districts", func(router fiber.Router) { + router.Get("/", districtsController.All) + router.Get("/:id", districtsController.Show) + router.Post("/", districtsController.Save) + router.Put("/:id", districtsController.Update) + router.Delete("/:id", districtsController.Delete) + }) +} diff --git a/app/module/districts/mapper/districts.mapper.go b/app/module/districts/mapper/districts.mapper.go new file mode 100644 index 0000000..239dc2a --- /dev/null +++ b/app/module/districts/mapper/districts.mapper.go @@ -0,0 +1,17 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/districts/response" +) + +func DistrictsResponseMapper(districtsReq *entity.Districts) (districtsRes *res.DistrictsResponse) { + if districtsReq != nil { + districtsRes = &res.DistrictsResponse{ + ID: districtsReq.ID, + DisNam: districtsReq.DisName, + CityId: districtsReq.CityId, + } + } + return districtsRes +} diff --git a/app/module/districts/repository/districts.repository.go b/app/module/districts/repository/districts.repository.go new file mode 100644 index 0000000..a0ee5f8 --- /dev/null +++ b/app/module/districts/repository/districts.repository.go @@ -0,0 +1,69 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/districts/request" + "narasi-ahli-be/utils/paginator" +) + +type districtsRepository struct { + DB *database.Database +} + +// DistrictsRepository define interface of IDistrictsRepository +type DistrictsRepository interface { + GetAll(req request.DistrictsQueryRequest) (districtss []*entity.Districts, paging paginator.Pagination, err error) + FindOne(id uint) (districts *entity.Districts, err error) + Create(districts *entity.Districts) (err error) + Update(id uint, districts *entity.Districts) (err error) + Delete(id uint) (err error) +} + +func NewDistrictsRepository(db *database.Database) DistrictsRepository { + return &districtsRepository{ + DB: db, + } +} + +// implement interface of IDistrictsRepository +func (_i *districtsRepository) GetAll(req request.DistrictsQueryRequest) (districtss []*entity.Districts, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Districts{}) + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&districtss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *districtsRepository) FindOne(id uint) (districts *entity.Districts, err error) { + if err := _i.DB.DB.First(&districts, id).Error; err != nil { + return nil, err + } + + return districts, nil +} + +func (_i *districtsRepository) Create(districts *entity.Districts) (err error) { + return _i.DB.DB.Create(districts).Error +} + +func (_i *districtsRepository) Update(id uint, districts *entity.Districts) (err error) { + return _i.DB.DB.Model(&entity.Districts{}). + Where(&entity.Districts{ID: id}). + Updates(districts).Error +} + +func (_i *districtsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.Districts{}, id).Error +} diff --git a/app/module/districts/request/districts.request.go b/app/module/districts/request/districts.request.go new file mode 100644 index 0000000..daa733d --- /dev/null +++ b/app/module/districts/request/districts.request.go @@ -0,0 +1,42 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" +) + +type DistrictsGeneric interface { + ToEntity() +} + +type DistrictsQueryRequest struct { + DisName string `json:"disName" validate:"required"` + CityId int `json:"cityId" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type DistrictsCreateRequest struct { + DisName string `json:"disName" validate:"required"` + CityId int `json:"cityId" validate:"required"` +} + +func (req DistrictsCreateRequest) ToEntity() *entity.Districts { + return &entity.Districts{ + DisName: req.DisName, + CityId: req.CityId, + } +} + +type DistrictsUpdateRequest struct { + ID uint `json:"id" validate:"required"` + DisName string `json:"disName" validate:"required"` + CityId int `json:"cityId" validate:"required"` +} + +func (req DistrictsUpdateRequest) ToEntity() *entity.Districts { + return &entity.Districts{ + ID: req.ID, + DisName: req.DisName, + CityId: req.CityId, + } +} diff --git a/app/module/districts/response/districts.response.go b/app/module/districts/response/districts.response.go new file mode 100644 index 0000000..1efe096 --- /dev/null +++ b/app/module/districts/response/districts.response.go @@ -0,0 +1,7 @@ +package response + +type DistrictsResponse struct { + ID uint `json:"id"` + DisNam string `json:"dis_nam"` + CityId int `json:"city_id"` +} diff --git a/app/module/districts/service/districts.service.go b/app/module/districts/service/districts.service.go new file mode 100644 index 0000000..bf2f5ff --- /dev/null +++ b/app/module/districts/service/districts.service.go @@ -0,0 +1,73 @@ +package service + +import ( + "narasi-ahli-be/app/module/districts/mapper" + "narasi-ahli-be/app/module/districts/repository" + "narasi-ahli-be/app/module/districts/request" + "narasi-ahli-be/app/module/districts/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// DistrictsService +type districtsService struct { + Repo repository.DistrictsRepository + Log zerolog.Logger +} + +// DistrictsService define interface of IDistrictsService +type DistrictsService interface { + All(req request.DistrictsQueryRequest) (districts []*response.DistrictsResponse, paging paginator.Pagination, err error) + Show(id uint) (districts *response.DistrictsResponse, err error) + Save(req request.DistrictsCreateRequest) (err error) + Update(id uint, req request.DistrictsUpdateRequest) (err error) + Delete(id uint) error +} + +// NewDistrictsService init DistrictsService +func NewDistrictsService(repo repository.DistrictsRepository, log zerolog.Logger) DistrictsService { + + return &districtsService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of DistrictsService +func (_i *districtsService) All(req request.DistrictsQueryRequest) (districtss []*response.DistrictsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + districtss = append(districtss, mapper.DistrictsResponseMapper(result)) + } + + return +} + +func (_i *districtsService) Show(id uint) (districts *response.DistrictsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.DistrictsResponseMapper(result), nil +} + +func (_i *districtsService) Save(req request.DistrictsCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *districtsService) Update(id uint, req request.DistrictsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *districtsService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/education_history/controller/controller.go b/app/module/education_history/controller/controller.go new file mode 100644 index 0000000..84745be --- /dev/null +++ b/app/module/education_history/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/education_history/service" + +type Controller struct { + EducationHistory EducationHistoryController +} + +func NewController(EducationHistoryService service.EducationHistoryService) *Controller { + return &Controller{ + EducationHistory: NewEducationHistoryController(EducationHistoryService), + } +} diff --git a/app/module/education_history/controller/education_history.controller.go b/app/module/education_history/controller/education_history.controller.go new file mode 100644 index 0000000..96afdb7 --- /dev/null +++ b/app/module/education_history/controller/education_history.controller.go @@ -0,0 +1,243 @@ +package controller + +import ( + "narasi-ahli-be/app/module/education_history/request" + "narasi-ahli-be/app/module/education_history/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type educationHistoryController struct { + educationHistoryService service.EducationHistoryService +} + +type EducationHistoryController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + UploadCertificate(c *fiber.Ctx) error +} + +func NewEducationHistoryController(educationHistoryService service.EducationHistoryService) EducationHistoryController { + return &educationHistoryController{ + educationHistoryService: educationHistoryService, + } +} + +// All Education History +// @Summary Get all Education History +// @Description API for getting all Education History for authenticated user +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.EducationHistoryQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history [get] +func (_i *educationHistoryController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + reqContext := request.EducationHistoryQueryRequestContext{ + SchoolName: c.Query("schoolName"), + Major: c.Query("major"), + EducationLevel: c.Query("educationLevel"), + GraduationYear: c.Query("graduationYear"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + educationData, paging, err := _i.educationHistoryService.All(authHeader, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history list successfully retrieved"}, + Data: educationData, + Meta: paging, + }) +} + +// Show Education History +// @Summary Get one Education History +// @Description API for getting one Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Education History ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history/{id} [get] +func (_i *educationHistoryController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + educationData, err := _i.educationHistoryService.Show(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history successfully retrieved"}, + Data: educationData, + }) +} + +// Save Education History +// @Summary Create Education History +// @Description API for create Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.EducationHistoryCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history [post] +func (_i *educationHistoryController) Save(c *fiber.Ctx) error { + req := new(request.EducationHistoryCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + dataResult, err := _i.educationHistoryService.Save(authHeader, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history successfully created"}, + Data: dataResult, + }) +} + +// Update Education History +// @Summary Update Education History +// @Description API for update Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Education History ID" +// @Param payload body request.EducationHistoryUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history/{id} [put] +func (_i *educationHistoryController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.EducationHistoryUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.educationHistoryService.Update(authHeader, uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history successfully updated"}, + }) +} + +// Delete Education History +// @Summary Delete Education History +// @Description API for delete Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Education History ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history/{id} [delete] +func (_i *educationHistoryController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.educationHistoryService.Delete(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history successfully deleted"}, + }) +} + +// UploadCertificate Education History +// @Summary Upload Certificate for Education History +// @Description API for upload certificate image for Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Education History ID" +// @Param certificate formData file true "Certificate image file" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history/{id}/certificate [post] +func (_i *educationHistoryController) UploadCertificate(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.educationHistoryService.UploadCertificate(authHeader, uint(id), c) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Certificate successfully uploaded"}, + }) +} diff --git a/app/module/education_history/education_history.module.go b/app/module/education_history/education_history.module.go new file mode 100644 index 0000000..37c63d3 --- /dev/null +++ b/app/module/education_history/education_history.module.go @@ -0,0 +1,55 @@ +package education_history + +import ( + "narasi-ahli-be/app/module/education_history/controller" + "narasi-ahli-be/app/module/education_history/repository" + "narasi-ahli-be/app/module/education_history/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of EducationHistoryRouter +type EducationHistoryRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of EducationHistory module +var NewEducationHistoryModule = fx.Options( + // register repository of EducationHistory module + fx.Provide(repository.NewEducationHistoryRepository), + + // register service of EducationHistory module + fx.Provide(service.NewEducationHistoryService), + + // register controller of EducationHistory module + fx.Provide(controller.NewController), + + // register router of EducationHistory module + fx.Provide(NewEducationHistoryRouter), +) + +// init EducationHistoryRouter +func NewEducationHistoryRouter(fiber *fiber.App, controller *controller.Controller) *EducationHistoryRouter { + return &EducationHistoryRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of EducationHistory module +func (_i *EducationHistoryRouter) RegisterEducationHistoryRoutes() { + // define controllers + educationHistoryController := _i.Controller.EducationHistory + + // define routes + _i.App.Route("/education-history", func(router fiber.Router) { + router.Get("/", educationHistoryController.All) + router.Get("/:id", educationHistoryController.Show) + router.Post("/", educationHistoryController.Save) + router.Put("/:id", educationHistoryController.Update) + router.Delete("/:id", educationHistoryController.Delete) + router.Post("/:id/certificate", educationHistoryController.UploadCertificate) + }) +} diff --git a/app/module/education_history/mapper/education_history.mapper.go b/app/module/education_history/mapper/education_history.mapper.go new file mode 100644 index 0000000..23c9140 --- /dev/null +++ b/app/module/education_history/mapper/education_history.mapper.go @@ -0,0 +1,31 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/education_history/response" +) + +func EducationHistoryResponseMapper(educationHistory *entity.EducationHistory) *response.EducationHistoryResponse { + result := &response.EducationHistoryResponse{ + ID: educationHistory.ID, + UserID: educationHistory.UserID, + SchoolName: educationHistory.SchoolName, + Major: educationHistory.Major, + EducationLevel: educationHistory.EducationLevel, + GraduationYear: educationHistory.GraduationYear, + CertificateImage: educationHistory.CertificateImage, + CreatedAt: educationHistory.CreatedAt, + UpdatedAt: educationHistory.UpdatedAt, + } + + if educationHistory.User != nil { + result.User = &response.UserBasicInfo{ + ID: educationHistory.User.ID, + Username: educationHistory.User.Username, + Fullname: educationHistory.User.Fullname, + Email: educationHistory.User.Email, + } + } + + return result +} diff --git a/app/module/education_history/repository/education_history.repository.go b/app/module/education_history/repository/education_history.repository.go new file mode 100644 index 0000000..49b8457 --- /dev/null +++ b/app/module/education_history/repository/education_history.repository.go @@ -0,0 +1,87 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/education_history/request" + "narasi-ahli-be/utils/paginator" +) + +type educationHistoryRepository struct { + DB *database.Database +} + +type EducationHistoryRepository interface { + GetAll(userId uint, req request.EducationHistoryQueryRequest) (educationHistories []*entity.EducationHistory, paging paginator.Pagination, err error) + FindOneByUserAndId(userId uint, id uint) (educationHistory *entity.EducationHistory, err error) + Create(educationHistory *entity.EducationHistory) (result *entity.EducationHistory, err error) + Update(userId uint, id uint, educationHistory *entity.EducationHistory) (err error) + Delete(userId uint, id uint) (err error) +} + +func NewEducationHistoryRepository(db *database.Database) EducationHistoryRepository { + return &educationHistoryRepository{ + DB: db, + } +} + +func (_i *educationHistoryRepository) GetAll(userId uint, req request.EducationHistoryQueryRequest) (educationHistories []*entity.EducationHistory, paging paginator.Pagination, err error) { + query := _i.DB.DB.Where("user_id = ?", userId) + + // Apply filters + if req.SchoolName != nil { + query = query.Where("school_name ILIKE ?", "%"+*req.SchoolName+"%") + } + if req.Major != nil { + query = query.Where("major ILIKE ?", "%"+*req.Major+"%") + } + if req.EducationLevel != nil { + query = query.Where("education_level = ?", *req.EducationLevel) + } + if req.GraduationYear != nil { + query = query.Where("graduation_year = ?", *req.GraduationYear) + } + + // Include user relationship + query = query.Preload("User") + + // Order by graduation year desc + query = query.Order("graduation_year DESC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&educationHistories).Error + paging = *req.Pagination + + return +} + +func (_i *educationHistoryRepository) FindOneByUserAndId(userId uint, id uint) (educationHistory *entity.EducationHistory, err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Preload("User").First(&educationHistory).Error + return +} + +func (_i *educationHistoryRepository) Create(educationHistory *entity.EducationHistory) (result *entity.EducationHistory, err error) { + err = _i.DB.DB.Create(educationHistory).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.DB.Preload("User").First(&result, educationHistory.ID).Error + return +} + +func (_i *educationHistoryRepository) Update(userId uint, id uint, educationHistory *entity.EducationHistory) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Updates(educationHistory).Error + return +} + +func (_i *educationHistoryRepository) Delete(userId uint, id uint) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Delete(&entity.EducationHistory{}).Error + return +} diff --git a/app/module/education_history/request/education_history.request.go b/app/module/education_history/request/education_history.request.go new file mode 100644 index 0000000..c30efe4 --- /dev/null +++ b/app/module/education_history/request/education_history.request.go @@ -0,0 +1,90 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" +) + +type EducationHistoryQueryRequest struct { + SchoolName *string `json:"schoolName"` + Major *string `json:"major"` + EducationLevel *string `json:"educationLevel"` + GraduationYear *int `json:"graduationYear"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type EducationHistoryCreateRequest struct { + SchoolName string `json:"schoolName" validate:"required,min=2,max=255"` + Major string `json:"major" validate:"required,min=2,max=255"` + EducationLevel string `json:"educationLevel" validate:"required,min=2,max=100"` + GraduationYear int `json:"graduationYear" validate:"required,min=1950,max=2030"` + CertificateImage string `json:"certificateImage,omitempty"` +} + +func (req EducationHistoryCreateRequest) ToEntity() *entity.EducationHistory { + certificateImage := &req.CertificateImage + if req.CertificateImage == "" { + certificateImage = nil + } + + return &entity.EducationHistory{ + SchoolName: req.SchoolName, + Major: req.Major, + EducationLevel: req.EducationLevel, + GraduationYear: req.GraduationYear, + CertificateImage: certificateImage, + } +} + +type EducationHistoryUpdateRequest struct { + SchoolName string `json:"schoolName" validate:"required,min=2,max=255"` + Major string `json:"major" validate:"required,min=2,max=255"` + EducationLevel string `json:"educationLevel" validate:"required,min=2,max=100"` + GraduationYear int `json:"graduationYear" validate:"required,min=1950,max=2030"` + CertificateImage string `json:"certificateImage,omitempty"` +} + +func (req EducationHistoryUpdateRequest) ToEntity() *entity.EducationHistory { + certificateImage := &req.CertificateImage + if req.CertificateImage == "" { + certificateImage = nil + } + + return &entity.EducationHistory{ + SchoolName: req.SchoolName, + Major: req.Major, + EducationLevel: req.EducationLevel, + GraduationYear: req.GraduationYear, + CertificateImage: certificateImage, + } +} + +type EducationHistoryQueryRequestContext struct { + SchoolName string `json:"schoolName"` + Major string `json:"major"` + EducationLevel string `json:"educationLevel"` + GraduationYear string `json:"graduationYear"` +} + +func (req EducationHistoryQueryRequestContext) ToParamRequest() EducationHistoryQueryRequest { + var request EducationHistoryQueryRequest + + if schoolName := req.SchoolName; schoolName != "" { + request.SchoolName = &schoolName + } + if major := req.Major; major != "" { + request.Major = &major + } + if educationLevel := req.EducationLevel; educationLevel != "" { + request.EducationLevel = &educationLevel + } + if graduationYearStr := req.GraduationYear; graduationYearStr != "" { + graduationYear, err := strconv.Atoi(graduationYearStr) + if err == nil { + request.GraduationYear = &graduationYear + } + } + + return request +} diff --git a/app/module/education_history/response/education_history.response.go b/app/module/education_history/response/education_history.response.go new file mode 100644 index 0000000..a6940c5 --- /dev/null +++ b/app/module/education_history/response/education_history.response.go @@ -0,0 +1,25 @@ +package response + +import ( + "time" +) + +type EducationHistoryResponse struct { + ID uint `json:"id"` + UserID uint `json:"userId"` + SchoolName string `json:"schoolName"` + Major string `json:"major"` + EducationLevel string `json:"educationLevel"` + GraduationYear int `json:"graduationYear"` + CertificateImage *string `json:"certificateImage"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + User *UserBasicInfo `json:"user,omitempty"` +} + +type UserBasicInfo struct { + ID uint `json:"id"` + Username string `json:"username"` + Fullname string `json:"fullname"` + Email string `json:"email"` +} diff --git a/app/module/education_history/service/education_history.service.go b/app/module/education_history/service/education_history.service.go new file mode 100644 index 0000000..d0d3b8c --- /dev/null +++ b/app/module/education_history/service/education_history.service.go @@ -0,0 +1,218 @@ +package service + +import ( + "context" + "errors" + "fmt" + "math/rand" + "path/filepath" + "strconv" + "strings" + "time" + + "narasi-ahli-be/app/module/education_history/mapper" + "narasi-ahli-be/app/module/education_history/repository" + "narasi-ahli-be/app/module/education_history/request" + "narasi-ahli-be/app/module/education_history/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +type educationHistoryService struct { + Repo repository.EducationHistoryRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger + Cfg *config.Config + MinioStorage *minioStorage.MinioStorage +} + +type EducationHistoryService interface { + All(authToken string, req request.EducationHistoryQueryRequest) (educationHistories []*response.EducationHistoryResponse, paging paginator.Pagination, err error) + Show(authToken string, id uint) (educationHistory *response.EducationHistoryResponse, err error) + Save(authToken string, req request.EducationHistoryCreateRequest) (educationHistory *response.EducationHistoryResponse, err error) + Update(authToken string, id uint, req request.EducationHistoryUpdateRequest) (err error) + Delete(authToken string, id uint) error + UploadCertificate(authToken string, id uint, c *fiber.Ctx) error +} + +func NewEducationHistoryService(repo repository.EducationHistoryRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger, cfg *config.Config, minioStorage *minioStorage.MinioStorage) EducationHistoryService { + return &educationHistoryService{ + Repo: repo, + UsersRepo: usersRepo, + Log: log, + Cfg: cfg, + MinioStorage: minioStorage, + } +} + +func (_i *educationHistoryService) All(authToken string, req request.EducationHistoryQueryRequest) (educationHistories []*response.EducationHistoryResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, paginator.Pagination{}, errors.New("unauthorized") + } + + results, paging, err := _i.Repo.GetAll(userInfo.ID, req) + if err != nil { + return + } + + for _, result := range results { + educationHistories = append(educationHistories, mapper.EducationHistoryResponseMapper(result)) + } + + return +} + +func (_i *educationHistoryService) Show(authToken string, id uint) (educationHistory *response.EducationHistoryResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("unauthorized") + } + + result, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return nil, err + } + + return mapper.EducationHistoryResponseMapper(result), nil +} + +func (_i *educationHistoryService) Save(authToken string, req request.EducationHistoryCreateRequest) (educationHistory *response.EducationHistoryResponse, err error) { + _i.Log.Info().Interface("data", req).Msg("Creating education history") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("unauthorized") + } + + entity := req.ToEntity() + entity.UserID = userInfo.ID + + result, err := _i.Repo.Create(entity) + if err != nil { + return nil, err + } + + return mapper.EducationHistoryResponseMapper(result), nil +} + +func (_i *educationHistoryService) Update(authToken string, id uint, req request.EducationHistoryUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Updating education history") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("education history not found") + } + + entity := req.ToEntity() + return _i.Repo.Update(userInfo.ID, id, entity) +} + +func (_i *educationHistoryService) Delete(authToken string, id uint) error { + _i.Log.Info().Str("authToken", authToken).Uint("id", id).Msg("Deleting education history") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("education history not found") + } + + return _i.Repo.Delete(userInfo.ID, id) +} + +func (_i *educationHistoryService) UploadCertificate(authToken string, id uint, c *fiber.Ctx) error { + _i.Log.Info().Str("authToken", authToken).Uint("id", id).Msg("Uploading certificate") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("education history not found") + } + + // Get multipart form + form, err := c.MultipartForm() + if err != nil { + return err + } + + // Get file from form + files := form.File["certificate"] + if len(files) == 0 { + return errors.New("no certificate file provided") + } + + fileHeader := files[0] + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + // Create minio connection + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + // Process file + src, err := fileHeader.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(fileHeader.Filename) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(fileHeader.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + + objectName := fmt.Sprintf("education-history/certificates/%d/%d/%s", now.Year(), now.Month(), newFilename) + + // Upload file to MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + + // Update certificate image path with MinIO object name + existing.CertificateImage = &objectName + return _i.Repo.Update(userInfo.ID, id, existing) +} diff --git a/app/module/feedbacks/controller/controller.go b/app/module/feedbacks/controller/controller.go new file mode 100644 index 0000000..11b66d9 --- /dev/null +++ b/app/module/feedbacks/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/feedbacks/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + Feedbacks FeedbacksController +} + +func NewController(FeedbacksService service.FeedbacksService, log zerolog.Logger) *Controller { + return &Controller{ + Feedbacks: NewFeedbacksController(FeedbacksService, log), + } +} diff --git a/app/module/feedbacks/controller/feedbacks.controller.go b/app/module/feedbacks/controller/feedbacks.controller.go new file mode 100644 index 0000000..6cf964d --- /dev/null +++ b/app/module/feedbacks/controller/feedbacks.controller.go @@ -0,0 +1,238 @@ +package controller + +import ( + "narasi-ahli-be/app/module/feedbacks/request" + "narasi-ahli-be/app/module/feedbacks/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" +) + +type feedbacksController struct { + feedbacksService service.FeedbacksService + Log zerolog.Logger +} + +type FeedbacksController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + FeedbackMonthlyStats(c *fiber.Ctx) error +} + +func NewFeedbacksController(feedbacksService service.FeedbacksService, log zerolog.Logger) FeedbacksController { + return &feedbacksController{ + feedbacksService: feedbacksService, + Log: log, + } +} + +// All get all Feedbacks +// @Summary Get all Feedbacks +// @Description API for getting all Feedbacks +// @Tags Feedbacks +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.FeedbacksQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /feedbacks [get] +func (_i *feedbacksController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.FeedbacksQueryRequestContext{ + Message: c.Query("message"), + CommentFromName: c.Query("commentFromName"), + CommentFromEmail: c.Query("commentFromEmail"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + feedbacksData, paging, err := _i.feedbacksService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Feedbacks list successfully retrieved"}, + Data: feedbacksData, + Meta: paging, + }) +} + +// Show get one Feedbacks +// @Summary Get one Feedbacks +// @Description API for getting one Feedbacks +// @Tags Feedbacks +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Feedbacks ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /feedbacks/{id} [get] +func (_i *feedbacksController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + feedbacksData, err := _i.feedbacksService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Feedbacks successfully retrieved"}, + Data: feedbacksData, + }) +} + +// Save create Feedbacks +// @Summary Create Feedbacks +// @Description API for create Feedbacks +// @Tags Feedbacks +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.FeedbacksCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /feedbacks [post] +func (_i *feedbacksController) Save(c *fiber.Ctx) error { + req := new(request.FeedbacksCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + dataResult, err := _i.feedbacksService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Feedbacks successfully created"}, + Data: dataResult, + }) +} + +// Update update Feedbacks +// @Summary update Feedbacks +// @Description API for update Feedbacks +// @Tags Feedbacks +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.FeedbacksUpdateRequest true "Required payload" +// @Param id path int true "Feedbacks ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /feedbacks/{id} [put] +func (_i *feedbacksController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.FeedbacksUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.feedbacksService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Feedbacks successfully updated"}, + }) +} + +// Delete delete Feedbacks +// @Summary delete Feedbacks +// @Description API for delete Feedbacks +// @Tags Feedbacks +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Feedbacks ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /feedbacks/{id} [delete] +func (_i *feedbacksController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.feedbacksService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Feedbacks successfully deleted"}, + }) +} + +// FeedbackMonthlyStats Feedbacks +// @Summary FeedbackMonthlyStats Feedbacks +// @Description API for FeedbackMonthlyStats of Feedbacks +// @Tags Feedbacks +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param year query int false "year" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /feedbacks/statistic/monthly [get] +func (_i *feedbacksController) FeedbackMonthlyStats(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + year := c.Query("year") + yearInt, err := strconv.Atoi(year) + if err != nil { + return err + } + + response, err := _i.feedbacksService.FeedbackMonthlyStats(authToken, &yearInt) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"FeedbacksMonthlyStats of Feedbacks successfully retrieved"}, + Data: response, + }) +} diff --git a/app/module/feedbacks/feedbacks.module.go b/app/module/feedbacks/feedbacks.module.go new file mode 100644 index 0000000..d30882a --- /dev/null +++ b/app/module/feedbacks/feedbacks.module.go @@ -0,0 +1,55 @@ +package feedbacks + +import ( + "narasi-ahli-be/app/module/feedbacks/controller" + "narasi-ahli-be/app/module/feedbacks/repository" + "narasi-ahli-be/app/module/feedbacks/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of FeedbacksRouter +type FeedbacksRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Feedbacks module +var NewFeedbacksModule = fx.Options( + // register repository of Feedbacks module + fx.Provide(repository.NewFeedbacksRepository), + + // register service of Feedbacks module + fx.Provide(service.NewFeedbacksService), + + // register controller of Feedbacks module + fx.Provide(controller.NewController), + + // register router of Feedbacks module + fx.Provide(NewFeedbacksRouter), +) + +// init FeedbacksRouter +func NewFeedbacksRouter(fiber *fiber.App, controller *controller.Controller) *FeedbacksRouter { + return &FeedbacksRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Feedbacks module +func (_i *FeedbacksRouter) RegisterFeedbacksRoutes() { + // define controllers + feedbacksController := _i.Controller.Feedbacks + + // define routes + _i.App.Route("/feedbacks", func(router fiber.Router) { + router.Get("/", feedbacksController.All) + router.Get("/:id", feedbacksController.Show) + router.Post("/", feedbacksController.Save) + router.Put("/:id", feedbacksController.Update) + router.Delete("/:id", feedbacksController.Delete) + router.Get("/statistic/monthly", feedbacksController.FeedbackMonthlyStats) + }) +} diff --git a/app/module/feedbacks/mapper/feedbacks.mapper.go b/app/module/feedbacks/mapper/feedbacks.mapper.go new file mode 100644 index 0000000..f19a589 --- /dev/null +++ b/app/module/feedbacks/mapper/feedbacks.mapper.go @@ -0,0 +1,24 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/feedbacks/response" +) + +func FeedbacksResponseMapper(feedbacksReq *entity.Feedbacks) (feedbacksRes *res.FeedbacksResponse) { + if feedbacksReq != nil { + feedbacksRes = &res.FeedbacksResponse{ + ID: feedbacksReq.ID, + Message: feedbacksReq.Message, + CommentFromName: feedbacksReq.CommentFromName, + CommentFromEmail: feedbacksReq.CommentFromEmail, + StatusId: feedbacksReq.StatusId, + ApprovedAt: feedbacksReq.ApprovedAt, + ReplyMessage: feedbacksReq.ReplyMessage, + IsActive: feedbacksReq.IsActive, + CreatedAt: feedbacksReq.CreatedAt, + UpdatedAt: feedbacksReq.UpdatedAt, + } + } + return feedbacksRes +} diff --git a/app/module/feedbacks/repository/feedbacks.repository.go b/app/module/feedbacks/repository/feedbacks.repository.go new file mode 100644 index 0000000..0d0cb26 --- /dev/null +++ b/app/module/feedbacks/repository/feedbacks.repository.go @@ -0,0 +1,162 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/feedbacks/request" + "narasi-ahli-be/app/module/feedbacks/response" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + "time" + + "github.com/rs/zerolog" +) + +type feedbacksRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// FeedbacksRepository define interface of IFeedbacksRepository +type FeedbacksRepository interface { + GetAll(req request.FeedbacksQueryRequest) (feedbackss []*entity.Feedbacks, paging paginator.Pagination, err error) + FindOne(id uint) (feedbacks *entity.Feedbacks, err error) + Create(feedbacks *entity.Feedbacks) (feedbacksReturn *entity.Feedbacks, err error) + Update(id uint, feedbacks *entity.Feedbacks) (err error) + Delete(id uint) (err error) + FeedbacksMonthlyStats(year int) (feedbacksMonthlyStats []*response.FeedbacksMonthlyStats, err error) +} + +func NewFeedbacksRepository(db *database.Database, logger zerolog.Logger) FeedbacksRepository { + return &feedbacksRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of IFeedbacksRepository +func (_i *feedbacksRepository) GetAll(req request.FeedbacksQueryRequest) (feedbackss []*entity.Feedbacks, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Feedbacks{}) + + query = query.Where("is_active = ?", true) + + if req.Message != nil && *req.Message != "" { + message := strings.ToLower(*req.Message) + query = query.Where("LOWER(message) LIKE ?", "%"+strings.ToLower(message)+"%") + } + if req.CommentFromName != nil && *req.CommentFromName != "" { + commentFromName := strings.ToLower(*req.CommentFromName) + query = query.Where("LOWER(comment_from_name) LIKE ?", "%"+strings.ToLower(commentFromName)+"%") + } + if req.CommentFromEmail != nil && *req.CommentFromEmail != "" { + commentFromEmail := strings.ToLower(*req.CommentFromEmail) + query = query.Where("LOWER(comment_from_email) LIKE ?", "%"+strings.ToLower(commentFromEmail)+"%") + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&feedbackss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *feedbacksRepository) FindOne(id uint) (feedbacks *entity.Feedbacks, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.First(&feedbacks).Error; err != nil { + return nil, err + } + + return feedbacks, nil +} + +func (_i *feedbacksRepository) Create(feedbacks *entity.Feedbacks) (feedbacksReturn *entity.Feedbacks, err error) { + result := _i.DB.DB.Create(feedbacks) + return feedbacks, result.Error +} + +func (_i *feedbacksRepository) Update(id uint, feedbacks *entity.Feedbacks) (err error) { + feedbacksMap, err := utilSvc.StructToMap(feedbacks) + if err != nil { + return err + } + query := _i.DB.DB.Model(&entity.Feedbacks{}).Where(&entity.Feedbacks{ID: id}) + return query.Updates(feedbacksMap).Error +} + +func (_i *feedbacksRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&entity.Feedbacks{}).Where("id = ?", id) + return query.Delete(&entity.Feedbacks{}).Error +} + +func (_i *feedbacksRepository) FeedbacksMonthlyStats(year int) (feedbacksMonthlyStats []*response.FeedbacksMonthlyStats, err error) { + + if year < 1900 || year > 2100 { + return nil, fmt.Errorf("invalid year") + } + + var results []struct { + Month int + Day int + TotalFeedbacks int + } + + query := _i.DB.DB.Model(&entity.Feedbacks{}). + Select("EXTRACT(MONTH FROM created_at) as month, EXTRACT(DAY FROM created_at) as day, "+ + "count(id) as total_feedbacks"). + Where("EXTRACT(YEAR FROM created_at) = ?", year) + + + err = query.Group("month, day").Scan(&results).Error + if err != nil { + return nil, err + } + + // Siapkan struktur untuk menyimpan data bulanan + feedbackStats := make([]*response.FeedbacksMonthlyStats, 12) + for i := 0; i < 12; i++ { + daysInMonth := time.Date(year, time.Month(i+1), 0, 0, 0, 0, 0, time.UTC).Day() + feedbackStats[i] = &response.FeedbacksMonthlyStats{ + Year: year, + Month: i + 1, + Suggestions: make([]int, daysInMonth), + } + } + + // Isi data dari hasil agregasi + for _, result := range results { + monthIndex := result.Month - 1 + dayIndex := result.Day - 1 + + if monthIndex >= 0 && monthIndex < 12 { + if dayIndex >= 0 && dayIndex < len(feedbackStats[monthIndex].Suggestions) { + feedbackStats[monthIndex].Suggestions[dayIndex] = result.TotalFeedbacks + } + } + } + + return feedbackStats, nil +} diff --git a/app/module/feedbacks/request/feedbacks.request.go b/app/module/feedbacks/request/feedbacks.request.go new file mode 100644 index 0000000..d79beef --- /dev/null +++ b/app/module/feedbacks/request/feedbacks.request.go @@ -0,0 +1,84 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type FeedbacksGeneric interface { + ToEntity() +} + +type FeedbacksQueryRequest struct { + Message *string `json:"message"` + CommentFromName *string `json:"commentFromName"` + CommentFromEmail *string `json:"commentFromEmail"` + StartDate *string `json:"startDate"` + EndDate *string `json:"endDate"` + StatusId *int `json:"statusId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type FeedbacksCreateRequest struct { + Message string `json:"message" validate:"required"` + CommentFromName string `json:"commentFromName" validate:"required"` + CommentFromEmail string `json:"commentFromEmail" validate:"required"` +} + +func (req FeedbacksCreateRequest) ToEntity() *entity.Feedbacks { + return &entity.Feedbacks{ + Message: req.Message, + CommentFromName: req.CommentFromName, + CommentFromEmail: req.CommentFromEmail, + StatusId: 0, + IsActive: true, + } +} + +type FeedbacksUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Message string `json:"message" validate:"required"` + CommentFromName string `json:"commentFromName" validate:"required"` + CommentFromEmail string `json:"commentFromEmail" validate:"required"` +} + +func (req FeedbacksUpdateRequest) ToEntity() *entity.Feedbacks { + return &entity.Feedbacks{ + ID: req.ID, + Message: req.Message, + CommentFromName: req.CommentFromName, + CommentFromEmail: req.CommentFromEmail, + UpdatedAt: time.Now(), + } +} + +type FeedbacksQueryRequestContext struct { + Message string `json:"message"` + CommentFromName string `json:"commentFromName"` + CommentFromEmail string `json:"commentFromEmail"` + StatusId string `json:"statusId"` +} + +func (req FeedbacksQueryRequestContext) ToParamRequest() FeedbacksQueryRequest { + var request FeedbacksQueryRequest + + if message := req.Message; message != "" { + request.Message = &message + } + if commentFromName := req.CommentFromName; commentFromName != "" { + request.CommentFromName = &commentFromName + } + if commentFromEmail := req.CommentFromEmail; commentFromEmail != "" { + request.CommentFromEmail = &commentFromEmail + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/feedbacks/response/feedbacks.response.go b/app/module/feedbacks/response/feedbacks.response.go new file mode 100644 index 0000000..1c72ff4 --- /dev/null +++ b/app/module/feedbacks/response/feedbacks.response.go @@ -0,0 +1,22 @@ +package response + +import "time" + +type FeedbacksResponse struct { + ID uint `json:"id"` + Message string `json:"message"` + CommentFromName string `json:"commentFromName"` + CommentFromEmail string `json:"commentFromEmail"` + StatusId int `json:"statusId"` + ApprovedAt *time.Time `json:"approvedAt"` + ReplyMessage *string `json:"replyMessage"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type FeedbacksMonthlyStats struct { + Year int `json:"year"` + Month int `json:"month"` + Suggestions []int `json:"suggestions"` +} diff --git a/app/module/feedbacks/service/feedbacks.service.go b/app/module/feedbacks/service/feedbacks.service.go new file mode 100644 index 0000000..37bc1d0 --- /dev/null +++ b/app/module/feedbacks/service/feedbacks.service.go @@ -0,0 +1,106 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/feedbacks/mapper" + "narasi-ahli-be/app/module/feedbacks/repository" + "narasi-ahli-be/app/module/feedbacks/request" + "narasi-ahli-be/app/module/feedbacks/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// FeedbacksService +type feedbacksService struct { + Repo repository.FeedbacksRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +// FeedbacksService define interface of IFeedbacksService +type FeedbacksService interface { + All(req request.FeedbacksQueryRequest) (feedbacks []*response.FeedbacksResponse, paging paginator.Pagination, err error) + Show(id uint) (feedbacks *response.FeedbacksResponse, err error) + Save(req request.FeedbacksCreateRequest, authToken string) (feedbacks *entity.Feedbacks, err error) + Update(id uint, req request.FeedbacksUpdateRequest) (err error) + Delete(id uint) error + FeedbackMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.FeedbacksMonthlyStats, err error) +} + +// NewFeedbacksService init FeedbacksService +func NewFeedbacksService(repo repository.FeedbacksRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) FeedbacksService { + + return &feedbacksService{ + Repo: repo, + Log: log, + UsersRepo: usersRepo, + } +} + +// All implement interface of FeedbacksService +func (_i *feedbacksService) All(req request.FeedbacksQueryRequest) (feedbackss []*response.FeedbacksResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + feedbackss = append(feedbackss, mapper.FeedbacksResponseMapper(result)) + } + + return +} + +func (_i *feedbacksService) Show(id uint) (feedbacks *response.FeedbacksResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.FeedbacksResponseMapper(result), nil +} + +func (_i *feedbacksService) Save(req request.FeedbacksCreateRequest, authToken string) (feedbacks *entity.Feedbacks, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + entity := req.ToEntity() + + return _i.Repo.Create(entity) +} + +func (_i *feedbacksService) Update(id uint, req request.FeedbacksUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + entity := req.ToEntity() + + return _i.Repo.Update(id, entity) +} + +func (_i *feedbacksService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + result.IsActive = false + return _i.Repo.Update(id, result) +} + +func (_i *feedbacksService) FeedbackMonthlyStats(authToken string, year *int) (articleMonthlyStats []*response.FeedbacksMonthlyStats, err error) { + //user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + + //var userLevelId *uint + //var userLevelNumber *int + // + //if user != nil { + // userLevelId = &user.UserLevelId + // userLevelNumber = &user.UserLevel.LevelNumber + //} + + result, err := _i.Repo.FeedbacksMonthlyStats(*year) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/app/module/magazine_files/controller/controller.go b/app/module/magazine_files/controller/controller.go new file mode 100644 index 0000000..785e9f8 --- /dev/null +++ b/app/module/magazine_files/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/magazine_files/service" + +type Controller struct { + MagazineFiles MagazineFilesController +} + +func NewController(MagazineFilesService service.MagazineFilesService) *Controller { + return &Controller{ + MagazineFiles: NewMagazineFilesController(MagazineFilesService), + } +} diff --git a/app/module/magazine_files/controller/magazine_files.controller.go b/app/module/magazine_files/controller/magazine_files.controller.go new file mode 100644 index 0000000..1a7f08e --- /dev/null +++ b/app/module/magazine_files/controller/magazine_files.controller.go @@ -0,0 +1,209 @@ +package controller + +import ( + "narasi-ahli-be/app/module/magazine_files/request" + "narasi-ahli-be/app/module/magazine_files/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type magazineFilesController struct { + magazineFilesService service.MagazineFilesService +} + +type MagazineFilesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error +} + +func NewMagazineFilesController(magazineFilesService service.MagazineFilesService) MagazineFilesController { + return &magazineFilesController{ + magazineFilesService: magazineFilesService, + } +} + +// All MagazineFiles +// @Summary Get all MagazineFiles +// @Description API for getting all MagazineFiles +// @Tags Magazine Files +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazine-files [get] +func (_i *magazineFilesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.MagazineFilesQueryRequestContext{ + Title: c.Query("title"), + Description: c.Query("description"), + MagazineId: c.Query("magazineId"), + StatusId: c.Query("statusId"), + IsPublish: c.Query("isPublish"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + magazineFilesData, paging, err := _i.magazineFilesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MagazineFiles list successfully retrieved"}, + Data: magazineFilesData, + Meta: paging, + }) +} + +// Show MagazineFiles +// @Summary Get one MagazineFiles +// @Description API for getting one MagazineFiles +// @Tags Magazine Files +// @Security Bearer +// @Param id path int true "MagazineFiles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazine-files/{id} [get] +func (_i *magazineFilesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + magazineFilesData, err := _i.magazineFilesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MagazineFiles successfully retrieved"}, + Data: magazineFilesData, + }) +} + +// Save MagazineFiles +// @Summary Create MagazineFiles +// @Description API for create MagazineFiles +// @Tags Magazine Files +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param files formData file true "Upload file" multiple true +// @Param title formData string true "Magazine file title" +// @Param description formData string true "Magazine file description" +// @Param magazineId path int true "Magazine ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazine-files/{magazineId} [post] +func (_i *magazineFilesController) Save(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("magazineId"), 10, 0) + if err != nil { + return err + } + title := c.Params("title") + description := c.Params("description") + + err = _i.magazineFilesService.Save(c, uint(id), title, description) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MagazineFiles successfully created"}, + }) +} + +// Update MagazineFiles +// @Summary Update MagazineFiles +// @Description API for update MagazineFiles +// @Tags Magazine Files +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "MagazineFiles ID" +// @Body request.MagazineFilesUpdateRequest +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazine-files/{id} [put] +func (_i *magazineFilesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.MagazineFilesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.magazineFilesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MagazineFiles successfully updated"}, + }) +} + +// Delete MagazineFiles +// @Summary delete MagazineFiles +// @Description API for delete MagazineFiles +// @Tags Magazine Files +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "MagazineFiles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazine-files/{id} [delete] +func (_i *magazineFilesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.magazineFilesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MagazineFiles successfully deleted"}, + }) +} + +// Viewer MagazineFiles +// @Summary Create MagazineFiles +// @Description API for create MagazineFiles +// @Tags Magazine Files +// @Security Bearer +// @Param filename path string true "Magazine File Name" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazine-files/viewer/{filename} [get] +func (_i *magazineFilesController) Viewer(c *fiber.Ctx) error { + return _i.magazineFilesService.Viewer(c) +} diff --git a/app/module/magazine_files/magazine_files.module.go b/app/module/magazine_files/magazine_files.module.go new file mode 100644 index 0000000..8da27d2 --- /dev/null +++ b/app/module/magazine_files/magazine_files.module.go @@ -0,0 +1,55 @@ +package magazine_files + +import ( + "narasi-ahli-be/app/module/magazine_files/controller" + "narasi-ahli-be/app/module/magazine_files/repository" + "narasi-ahli-be/app/module/magazine_files/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of MagazineFilesRouter +type MagazineFilesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of MagazineFiles module +var NewMagazineFilesModule = fx.Options( + // register repository of MagazineFiles module + fx.Provide(repository.NewMagazineFilesRepository), + + // register service of MagazineFiles module + fx.Provide(service.NewMagazineFilesService), + + // register controller of MagazineFiles module + fx.Provide(controller.NewController), + + // register router of MagazineFiles module + fx.Provide(NewMagazineFilesRouter), +) + +// init MagazineFilesRouter +func NewMagazineFilesRouter(fiber *fiber.App, controller *controller.Controller) *MagazineFilesRouter { + return &MagazineFilesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of MagazineFiles module +func (_i *MagazineFilesRouter) RegisterMagazineFilesRoutes() { + // define controllers + magazineFilesController := _i.Controller.MagazineFiles + + // define routes + _i.App.Route("/magazine-files", func(router fiber.Router) { + router.Get("/", magazineFilesController.All) + router.Get("/:id", magazineFilesController.Show) + router.Post("/:magazineId", magazineFilesController.Save) + router.Put("/:id", magazineFilesController.Update) + router.Delete("/:id", magazineFilesController.Delete) + router.Get("/viewer/:filename", magazineFilesController.Viewer) + }) +} diff --git a/app/module/magazine_files/mapper/magazine_files.mapper.go b/app/module/magazine_files/mapper/magazine_files.mapper.go new file mode 100644 index 0000000..f9db420 --- /dev/null +++ b/app/module/magazine_files/mapper/magazine_files.mapper.go @@ -0,0 +1,37 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/magazine_files/response" +) + +func MagazineFilesResponseMapper(magazineFilesReq *entity.MagazineFiles, host string) (magazineFilesRes *res.MagazineFilesResponse) { + fileUrl := host + "/magazine-files/viewer/" + if magazineFilesReq.FileName != nil { + fileUrl += *magazineFilesReq.FileName + } + + if magazineFilesReq != nil { + magazineFilesRes = &res.MagazineFilesResponse{ + ID: magazineFilesReq.ID, + Title: magazineFilesReq.Title, + Description: magazineFilesReq.Description, + MagazineId: magazineFilesReq.MagazineId, + DownloadCount: magazineFilesReq.DownloadCount, + FilePath: magazineFilesReq.FilePath, + FileUrl: &fileUrl, + FileName: magazineFilesReq.FileName, + FileAlt: magazineFilesReq.FileAlt, + WidthPixel: magazineFilesReq.WidthPixel, + HeightPixel: magazineFilesReq.HeightPixel, + Size: magazineFilesReq.Size, + StatusId: magazineFilesReq.StatusId, + IsPublish: magazineFilesReq.IsPublish, + PublishedAt: magazineFilesReq.PublishedAt, + IsActive: magazineFilesReq.IsActive, + CreatedAt: magazineFilesReq.CreatedAt, + UpdatedAt: magazineFilesReq.UpdatedAt, + } + } + return magazineFilesRes +} diff --git a/app/module/magazine_files/repository/magazine_files.repository.go b/app/module/magazine_files/repository/magazine_files.repository.go new file mode 100644 index 0000000..732942c --- /dev/null +++ b/app/module/magazine_files/repository/magazine_files.repository.go @@ -0,0 +1,121 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/magazine_files/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" +) + +type magazineFilesRepository struct { + DB *database.Database +} + +// MagazineFilesRepository define interface of IMagazineFilesRepository +type MagazineFilesRepository interface { + GetAll(req request.MagazineFilesQueryRequest) (magazineFiless []*entity.MagazineFiles, paging paginator.Pagination, err error) + FindOne(id uint) (magazineFiles *entity.MagazineFiles, err error) + FindByMagazine(magazineId uint) (magazineFiles []*entity.MagazineFiles, err error) + FindByFilename(filename string) (magazineFiles *entity.MagazineFiles, err error) + Create(magazineFiles *entity.MagazineFiles) (err error) + Update(id uint, magazineFiles *entity.MagazineFiles) (err error) + Delete(id uint) (err error) +} + +func NewMagazineFilesRepository(db *database.Database) MagazineFilesRepository { + return &magazineFilesRepository{ + DB: db, + } +} + +// implement interface of IMagazineFilesRepository +func (_i *magazineFilesRepository) GetAll(req request.MagazineFilesQueryRequest) (magazineFiless []*entity.MagazineFiles, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.MagazineFiles{}) + query = query.Where("is_active = ?", true) + + if req.Title != nil && *req.Title != "" { + title := strings.ToLower(*req.Title) + query = query.Where("LOWER(title) LIKE ?", "%"+strings.ToLower(title)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.MagazineId != nil { + query = query.Where("magazine_id = ?", req.MagazineId) + } + if req.IsPublish != nil { + query = query.Where("is_publish = ?", req.IsPublish) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&magazineFiless).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *magazineFilesRepository) FindByMagazine(magazineId uint) (magazineFiles []*entity.MagazineFiles, err error) { + if err := _i.DB.DB.Where("magazine_id = ?", magazineId).Find(&magazineFiles).Error; err != nil { + return nil, err + } + + return magazineFiles, nil +} + +func (_i *magazineFilesRepository) FindByFilename(magazineFilename string) (magazineFiles *entity.MagazineFiles, err error) { + + if err := _i.DB.DB.Where("file_name = ?", magazineFilename).First(&magazineFiles).Error; err != nil { + return nil, err + } + + return magazineFiles, nil +} + +func (_i *magazineFilesRepository) FindOne(id uint) (magazineFiles *entity.MagazineFiles, err error) { + if err := _i.DB.DB.First(&magazineFiles, id).Error; err != nil { + return nil, err + } + + return magazineFiles, nil +} + +func (_i *magazineFilesRepository) Create(magazineFiles *entity.MagazineFiles) (err error) { + return _i.DB.DB.Create(magazineFiles).Error +} + +func (_i *magazineFilesRepository) Update(id uint, magazineFiles *entity.MagazineFiles) (err error) { + magazineFilesMap, err := utilSvc.StructToMap(magazineFiles) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.MagazineFiles{}). + Where(&entity.MagazineFiles{ID: id}). + Updates(magazineFilesMap).Error +} + +func (_i *magazineFilesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.MagazineFiles{}, id).Error +} diff --git a/app/module/magazine_files/request/magazine_files.request.go b/app/module/magazine_files/request/magazine_files.request.go new file mode 100644 index 0000000..4c6a500 --- /dev/null +++ b/app/module/magazine_files/request/magazine_files.request.go @@ -0,0 +1,137 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type MagazineFilesGeneric interface { + ToEntity() +} + +type MagazineFilesQueryRequest struct { + Title *string `json:"title"` + Description *string `json:"description"` + MagazineId *int `json:"magazineId"` + DownloadCount *int `json:"downloadCount"` + StatusId *int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` + IsActive *bool `json:"isActive"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type MagazineFilesCreateRequest struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + MagazineId uint `json:"magazineId" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + DownloadCount *int `json:"downloadCount"` + FilePath *string `json:"filePath"` + FileUrl *string `json:"fileUrl"` + FileName *string `json:"fileName"` + FileAlt *string `json:"fileAlt"` + WidthPixel *string `json:"widthPixel"` + HeightPixel *string `json:"heightPixel"` + Size *string `json:"size"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` +} + +func (req MagazineFilesCreateRequest) ToEntity() *entity.MagazineFiles { + return &entity.MagazineFiles{ + Title: req.Title, + Description: req.Description, + MagazineId: req.MagazineId, + DownloadCount: req.DownloadCount, + FilePath: req.FilePath, + FileUrl: req.FileUrl, + FileName: req.FileName, + FileAlt: req.FileAlt, + WidthPixel: req.WidthPixel, + HeightPixel: req.HeightPixel, + Size: req.Size, + StatusId: req.StatusId, + IsPublish: req.IsPublish, + PublishedAt: req.PublishedAt, + } +} + +type MagazineFilesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + MagazineId uint `json:"magazineId" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + DownloadCount *int `json:"downloadCount"` + FilePath *string `json:"filePath"` + FileUrl *string `json:"fileUrl"` + FileName *string `json:"fileName"` + FileAlt *string `json:"fileAlt"` + WidthPixel *string `json:"widthPixel"` + HeightPixel *string `json:"heightPixel"` + Size *string `json:"size"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` +} + +func (req MagazineFilesUpdateRequest) ToEntity() *entity.MagazineFiles { + return &entity.MagazineFiles{ + ID: req.ID, + Title: req.Title, + Description: req.Description, + MagazineId: req.MagazineId, + DownloadCount: req.DownloadCount, + FilePath: req.FilePath, + FileUrl: req.FileUrl, + FileName: req.FileName, + FileAlt: req.FileAlt, + WidthPixel: req.WidthPixel, + HeightPixel: req.HeightPixel, + Size: req.Size, + StatusId: req.StatusId, + IsPublish: req.IsPublish, + PublishedAt: req.PublishedAt, + } +} + +type MagazineFilesQueryRequestContext struct { + Title string `json:"title"` + Description string `json:"description"` + MagazineId string `json:"magazineId"` + IsPublish string `json:"isPublish"` + StatusId string `json:"statusId"` +} + +func (req MagazineFilesQueryRequestContext) ToParamRequest() MagazineFilesQueryRequest { + var request MagazineFilesQueryRequest + + if title := req.Title; title != "" { + request.Title = &title + } + if description := req.Description; description != "" { + request.Description = &description + } + if magazineIdStr := req.MagazineId; magazineIdStr != "" { + magazineId, err := strconv.Atoi(magazineIdStr) + if err == nil { + request.MagazineId = &magazineId + } + } + if isPublishStr := req.IsPublish; isPublishStr != "" { + isPublish, err := strconv.ParseBool(isPublishStr) + if err == nil { + request.IsPublish = &isPublish + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/magazine_files/response/magazine_files.response.go b/app/module/magazine_files/response/magazine_files.response.go new file mode 100644 index 0000000..5b46c07 --- /dev/null +++ b/app/module/magazine_files/response/magazine_files.response.go @@ -0,0 +1,24 @@ +package response + +import "time" + +type MagazineFilesResponse struct { + ID uint `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + MagazineId uint `json:"magazineId"` + DownloadCount *int `json:"downloadCount"` + FilePath *string `json:"filePath"` + FileUrl *string `json:"fileUrl"` + FileName *string `json:"fileName"` + FileAlt *string `json:"fileAlt"` + WidthPixel *string `json:"widthPixel"` + HeightPixel *string `json:"heightPixel"` + Size *string `json:"size"` + StatusId int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/magazine_files/service/magazine_files.service.go b/app/module/magazine_files/service/magazine_files.service.go new file mode 100644 index 0000000..8133731 --- /dev/null +++ b/app/module/magazine_files/service/magazine_files.service.go @@ -0,0 +1,228 @@ +package service + +import ( + "context" + "fmt" + "io" + "log" + "math/rand" + "mime" + "narasi-ahli-be/app/module/magazine_files/mapper" + "narasi-ahli-be/app/module/magazine_files/repository" + "narasi-ahli-be/app/module/magazine_files/request" + "narasi-ahli-be/app/module/magazine_files/response" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +// MagazineFilesService +type magazineFilesService struct { + Repo repository.MagazineFilesRepository + Log zerolog.Logger + Cfg *config.Config + MinioStorage *minioStorage.MinioStorage +} + +// MagazineFilesService define interface of IMagazineFilesService +type MagazineFilesService interface { + All(req request.MagazineFilesQueryRequest) (magazineFiles []*response.MagazineFilesResponse, paging paginator.Pagination, err error) + Show(id uint) (magazineFiles *response.MagazineFilesResponse, err error) + Save(c *fiber.Ctx, id uint, title string, description string) (err error) + Update(id uint, req request.MagazineFilesUpdateRequest) (err error) + Delete(id uint) error + Viewer(c *fiber.Ctx) error +} + +// NewMagazineFilesService init MagazineFilesService +func NewMagazineFilesService(repo repository.MagazineFilesRepository, log zerolog.Logger, cfg *config.Config, minioStorage *minioStorage.MinioStorage) MagazineFilesService { + + return &magazineFilesService{ + Repo: repo, + Log: log, + Cfg: cfg, + MinioStorage: minioStorage, + } +} + +// All implement interface of MagazineFilesService +func (_i *magazineFilesService) All(req request.MagazineFilesQueryRequest) (magazineFiless []*response.MagazineFilesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + host := _i.Cfg.App.Domain + for _, result := range results { + magazineFiless = append(magazineFiless, mapper.MagazineFilesResponseMapper(result, host)) + } + return +} + +func (_i *magazineFilesService) Show(id uint) (magazineFiles *response.MagazineFilesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + host := _i.Cfg.App.Domain + return mapper.MagazineFilesResponseMapper(result, host), nil +} + +func (_i *magazineFilesService) Save(c *fiber.Ctx, id uint, title string, description string) (err error) { + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + + if err != nil { + return err + } + //filess := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + for _, files := range form.File { + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: top"). + Interface("files", files).Msg("") + + for _, fileHeader := range files { + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop"). + Interface("data", fileHeader).Msg("") + + src, err := fileHeader.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(fileHeader.Filename) + filenameAlt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(fileHeader.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + objectName := fmt.Sprintf("magazines/upload/%d/%d/%s", now.Year(), now.Month(), newFilename) + + fileSize := strconv.FormatInt(fileHeader.Size, 10) + + req := request.MagazineFilesCreateRequest{ + MagazineId: id, + Title: title, + Description: description, + FilePath: &objectName, + FileName: &newFilename, + FileAlt: &filenameAlt, + Size: &fileSize, + } + + err = _i.Repo.Create(req.ToEntity()) + if err != nil { + return err + } + + // Upload file ke MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + } + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "User:All"). + Interface("data", "Successfully uploaded").Msg("") + + return +} + +func (_i *magazineFilesService) Viewer(c *fiber.Ctx) (err error) { + filename := c.Params("filename") + result, err := _i.Repo.FindByFilename(filename) + if err != nil { + return err + } + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := *result.FilePath + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Article:Uploads"). + Interface("data", objectName).Msg("") + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + log.Fatalln(err) + } + defer fileContent.Close() + + // Tentukan Content-Type berdasarkan ekstensi file + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) + if contentType == "" { + contentType = "application/octet-stream" // fallback jika tidak ada tipe MIME yang cocok + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return +} + +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // jika tidak ada ekstensi, kembalikan string kosong + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // ambil ekstensi terakhir + return parts[len(parts)-1] +} + +func (_i *magazineFilesService) Update(id uint, req request.MagazineFilesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *magazineFilesService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/magazines/controller/controller.go b/app/module/magazines/controller/controller.go new file mode 100644 index 0000000..4746a8f --- /dev/null +++ b/app/module/magazines/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/magazines/service" + +type Controller struct { + Magazines MagazinesController +} + +func NewController(MagazinesService service.MagazinesService) *Controller { + return &Controller{ + Magazines: NewMagazinesController(MagazinesService), + } +} diff --git a/app/module/magazines/controller/magazines.controller.go b/app/module/magazines/controller/magazines.controller.go new file mode 100644 index 0000000..e501a77 --- /dev/null +++ b/app/module/magazines/controller/magazines.controller.go @@ -0,0 +1,244 @@ +package controller + +import ( + "narasi-ahli-be/app/module/magazines/request" + "narasi-ahli-be/app/module/magazines/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type magazinesController struct { + magazinesService service.MagazinesService +} + +type MagazinesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + SaveThumbnail(c *fiber.Ctx) error + Viewer(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewMagazinesController(magazinesService service.MagazinesService) MagazinesController { + return &magazinesController{ + magazinesService: magazinesService, + } +} + +// All Magazines +// @Summary Get all Magazines +// @Description API for getting all Magazines +// @Tags Magazines +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.MagazinesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines [get] +func (_i *magazinesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.MagazinesQueryRequestContext{ + Title: c.Query("title"), + Description: c.Query("description"), + CreatedById: c.Query("createdById"), + StatusId: c.Query("statusId"), + IsPublish: c.Query("isPublish"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + magazinesData, paging, err := _i.magazinesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Magazines list successfully retrieved"}, + Data: magazinesData, + Meta: paging, + }) +} + +// Show Magazines +// @Summary Get one Magazines +// @Description API for getting one Magazines +// @Tags Magazines +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Magazines ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines/{id} [get] +func (_i *magazinesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + magazinesData, err := _i.magazinesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Magazines successfully retrieved"}, + Data: magazinesData, + }) +} + +// Save Magazines +// @Summary Create Magazines +// @Description API for create Magazines +// @Tags Magazines +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.MagazinesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines [post] +func (_i *magazinesController) Save(c *fiber.Ctx) error { + req := new(request.MagazinesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + dataResult, err := _i.magazinesService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Magazines successfully created"}, + Data: dataResult, + }) +} + +// Update Magazines +// @Summary Update Magazines +// @Description API for update Magazines +// @Tags Magazines +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Magazines ID" +// @Param payload body request.MagazinesUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines/{id} [put] +func (_i *magazinesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.MagazinesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.magazinesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Magazines successfully updated"}, + }) +} + +// SaveThumbnail Magazines +// @Summary Save Thumbnail Magazines +// @Description API for Save Thumbnail of Magazines +// @Tags Magazines +// @Security Bearer +// @Produce json +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Magazine ID" +// @Param files formData file true "Upload thumbnail" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines/thumbnail/{id} [post] +func (_i *magazinesController) SaveThumbnail(c *fiber.Ctx) error { + err := _i.magazinesService.SaveThumbnail(c) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Thumbnail of Magazines successfully created"}, + }) +} + +// Viewer Magazines +// @Summary Viewer Magazines Thumbnail +// @Description API for View Thumbnail of Magazines +// @Tags Magazines +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param thumbnailName path string true "Magazines Thumbnail Name" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines/thumbnail/viewer/{thumbnailName} [get] +func (_i *magazinesController) Viewer(c *fiber.Ctx) error { + return _i.magazinesService.Viewer(c) +} + +// Delete Magazines +// @Summary Delete Magazines +// @Description API for delete Magazines +// @Tags Magazines +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Magazines ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /magazines/{id} [delete] +func (_i *magazinesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.magazinesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Magazines successfully deleted"}, + }) +} diff --git a/app/module/magazines/magazines.module.go b/app/module/magazines/magazines.module.go new file mode 100644 index 0000000..83f780d --- /dev/null +++ b/app/module/magazines/magazines.module.go @@ -0,0 +1,56 @@ +package magazines + +import ( + "narasi-ahli-be/app/module/magazines/controller" + "narasi-ahli-be/app/module/magazines/repository" + "narasi-ahli-be/app/module/magazines/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of MagazinesRouter +type MagazinesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Magazines module +var NewMagazinesModule = fx.Options( + // register repository of Magazines module + fx.Provide(repository.NewMagazinesRepository), + + // register service of Magazines module + fx.Provide(service.NewMagazinesService), + + // register controller of Magazines module + fx.Provide(controller.NewController), + + // register router of Magazines module + fx.Provide(NewMagazinesRouter), +) + +// init MagazinesRouter +func NewMagazinesRouter(fiber *fiber.App, controller *controller.Controller) *MagazinesRouter { + return &MagazinesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Magazines module +func (_i *MagazinesRouter) RegisterMagazinesRoutes() { + // define controllers + magazinesController := _i.Controller.Magazines + + // define routes + _i.App.Route("/magazines", func(router fiber.Router) { + router.Get("/", magazinesController.All) + router.Get("/:id", magazinesController.Show) + router.Post("/", magazinesController.Save) + router.Put("/:id", magazinesController.Update) + router.Post("/thumbnail/:id", magazinesController.SaveThumbnail) + router.Get("/thumbnail/viewer/:thumbnailName", magazinesController.Viewer) + router.Delete("/:id", magazinesController.Delete) + }) +} diff --git a/app/module/magazines/mapper/magazines.mapper.go b/app/module/magazines/mapper/magazines.mapper.go new file mode 100644 index 0000000..4765089 --- /dev/null +++ b/app/module/magazines/mapper/magazines.mapper.go @@ -0,0 +1,42 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + magazineFilesMapper "narasi-ahli-be/app/module/magazine_files/mapper" + magazineFilesRepository "narasi-ahli-be/app/module/magazine_files/repository" + magazineFilesResponse "narasi-ahli-be/app/module/magazine_files/response" + res "narasi-ahli-be/app/module/magazines/response" +) + +func MagazinesResponseMapper(magazinesReq *entity.Magazines, magazineFilesRepo magazineFilesRepository.MagazineFilesRepository, host string) (magazinesRes *res.MagazinesResponse) { + magazineFiles, _ := magazineFilesRepo.FindByMagazine(magazinesReq.ID) + var magazineFilesArr []*magazineFilesResponse.MagazineFilesResponse + if magazineFiles != nil && len(magazineFiles) > 0 { + for _, result := range magazineFiles { + magazineFilesArr = append(magazineFilesArr, magazineFilesMapper.MagazineFilesResponseMapper(result, host)) + } + } + + if magazinesReq != nil { + magazinesRes = &res.MagazinesResponse{ + ID: magazinesReq.ID, + Title: magazinesReq.Title, + Description: magazinesReq.Description, + ThumbnailPath: magazinesReq.ThumbnailPath, + PageUrl: magazinesReq.PageUrl, + CreatedById: magazinesReq.CreatedById, + StatusId: magazinesReq.StatusId, + IsPublish: magazinesReq.IsPublish, + PublishedAt: magazinesReq.PublishedAt, + IsActive: magazinesReq.IsActive, + CreatedAt: magazinesReq.CreatedAt, + UpdatedAt: magazinesReq.UpdatedAt, + MagazineFiles: magazineFilesArr, + } + + if magazinesReq.ThumbnailPath != nil { + magazinesRes.ThumbnailUrl = host + "/magazines/thumbnail/viewer/" + *magazinesReq.ThumbnailName + } + } + return magazinesRes +} diff --git a/app/module/magazines/repository/magazines.repository.go b/app/module/magazines/repository/magazines.repository.go new file mode 100644 index 0000000..1c460e1 --- /dev/null +++ b/app/module/magazines/repository/magazines.repository.go @@ -0,0 +1,118 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/magazines/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" +) + +type magazinesRepository struct { + DB *database.Database +} + +// MagazinesRepository define interface of IMagazinesRepository +type MagazinesRepository interface { + GetAll(req request.MagazinesQueryRequest) (magaziness []*entity.Magazines, paging paginator.Pagination, err error) + FindOne(id uint) (magazines *entity.Magazines, err error) + FindByFilename(thumbnailName string) (magazineReturn *entity.Magazines, err error) + Create(magazines *entity.Magazines) (magazineReturn *entity.Magazines, err error) + Update(id uint, magazines *entity.Magazines) (err error) + Delete(id uint) (err error) +} + +func NewMagazinesRepository(db *database.Database) MagazinesRepository { + return &magazinesRepository{ + DB: db, + } +} + +// implement interface of IMagazinesRepository +func (_i *magazinesRepository) GetAll(req request.MagazinesQueryRequest) (magaziness []*entity.Magazines, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Magazines{}) + + query = query.Where("is_active = ?", true) + + if req.Title != nil && *req.Title != "" { + title := strings.ToLower(*req.Title) + query = query.Where("LOWER(title) LIKE ?", "%"+strings.ToLower(title)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.CreatedById != nil { + query = query.Where("created_by_id = ?", req.CreatedById) + } + if req.IsPublish != nil { + query = query.Where("is_publish = ?", req.IsPublish) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&magaziness).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *magazinesRepository) FindOne(id uint) (magazines *entity.Magazines, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.First(&magazines).Error; err != nil { + return nil, err + } + + return magazines, nil +} + +func (_i *magazinesRepository) FindByFilename(thumbnailName string) (magazines *entity.Magazines, err error) { + query := _i.DB.DB.Where("thumbnail_name = ?", thumbnailName) + + if err := query.First(&magazines).Error; err != nil { + return nil, err + } + + return magazines, nil +} + +func (_i *magazinesRepository) Create(magazines *entity.Magazines) (magazineReturn *entity.Magazines, err error) { + result := _i.DB.DB.Create(magazines) + return magazines, result.Error +} + +func (_i *magazinesRepository) Update(id uint, magazines *entity.Magazines) (err error) { + magazinesMap, err := utilSvc.StructToMap(magazines) + if err != nil { + return err + } + query := _i.DB.DB.Model(&entity.Magazines{}).Where(&entity.Magazines{ID: id}) + return query.Updates(magazinesMap).Error +} + +func (_i *magazinesRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&entity.Magazines{}).Where("id = ?", id) + return query.Delete(&entity.Magazines{}).Error +} diff --git a/app/module/magazines/request/magazines.request.go b/app/module/magazines/request/magazines.request.go new file mode 100644 index 0000000..22725c0 --- /dev/null +++ b/app/module/magazines/request/magazines.request.go @@ -0,0 +1,119 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type MagazinesGeneric interface { + ToEntity() +} + +type MagazinesQueryRequest struct { + Title *string `json:"title"` + Description *string `json:"description"` + ThumbnailPath *string `json:"thumbnailPath"` + ThumbnailUrl *string `json:"thumbnailUrl"` + PageUrl *string `json:"pageUrl"` + CreatedById *int `json:"createdById"` + StatusId *int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type MagazinesCreateRequest struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + ThumbnailPath *string `json:"thumbnailPath"` + ThumbnailUrl *string `json:"thumbnailUrl"` + PageUrl *string `json:"pageUrl"` + CreatedById *uint `json:"createdById"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` +} + +func (req MagazinesCreateRequest) ToEntity() *entity.Magazines { + return &entity.Magazines{ + Title: req.Title, + Description: req.Description, + ThumbnailPath: req.ThumbnailPath, + ThumbnailUrl: req.ThumbnailUrl, + PageUrl: req.PageUrl, + CreatedById: req.CreatedById, + StatusId: req.StatusId, + IsPublish: req.IsPublish, + PublishedAt: req.PublishedAt, + IsActive: true, + } +} + +type MagazinesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + ThumbnailPath *string `json:"thumbnailPath"` + ThumbnailUrl *string `json:"thumbnailUrl"` + PageUrl *string `json:"pageUrl"` + CreatedById *uint `json:"createdById"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` +} + +func (req MagazinesUpdateRequest) ToEntity() *entity.Magazines { + return &entity.Magazines{ + ID: req.ID, + Title: req.Title, + Description: req.Description, + ThumbnailPath: req.ThumbnailPath, + ThumbnailUrl: req.ThumbnailUrl, + PageUrl: req.PageUrl, + CreatedById: req.CreatedById, + StatusId: req.StatusId, + IsPublish: req.IsPublish, + PublishedAt: req.PublishedAt, + UpdatedAt: time.Now(), + } +} + +type MagazinesQueryRequestContext struct { + Title string `json:"title"` + Description string `json:"description"` + CreatedById string `json:"createdById"` + IsPublish string `json:"isPublish"` + StatusId string `json:"statusId"` +} + +func (req MagazinesQueryRequestContext) ToParamRequest() MagazinesQueryRequest { + var request MagazinesQueryRequest + + if title := req.Title; title != "" { + request.Title = &title + } + if description := req.Description; description != "" { + request.Description = &description + } + if isPublishStr := req.IsPublish; isPublishStr != "" { + isPublish, err := strconv.ParseBool(isPublishStr) + if err == nil { + request.IsPublish = &isPublish + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + if createdByIdStr := req.CreatedById; createdByIdStr != "" { + createdById, err := strconv.Atoi(createdByIdStr) + if err == nil { + request.CreatedById = &createdById + } + } + + return request +} diff --git a/app/module/magazines/response/magazines.response.go b/app/module/magazines/response/magazines.response.go new file mode 100644 index 0000000..d1117d5 --- /dev/null +++ b/app/module/magazines/response/magazines.response.go @@ -0,0 +1,24 @@ +package response + +import ( + magazineFilesResponse "narasi-ahli-be/app/module/magazine_files/response" + "time" +) + +type MagazinesResponse struct { + ID uint `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + ThumbnailPath *string `json:"thumbnailPath"` + ThumbnailUrl string `json:"thumbnailUrl"` + PageUrl *string `json:"pageUrl"` + CreatedById *uint `json:"createdById"` + StatusId int `json:"statusId"` + IsPublish *bool `json:"isPublish"` + PublishedAt *time.Time `json:"publishedAt"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + MagazineFiles []*magazineFilesResponse.MagazineFilesResponse `json:"files"` +} diff --git a/app/module/magazines/service/magazines.service.go b/app/module/magazines/service/magazines.service.go new file mode 100644 index 0000000..192cb1c --- /dev/null +++ b/app/module/magazines/service/magazines.service.go @@ -0,0 +1,257 @@ +package service + +import ( + "context" + "fmt" + "io" + "log" + "math/rand" + "mime" + "narasi-ahli-be/app/database/entity" + magazineFilesRepository "narasi-ahli-be/app/module/magazine_files/repository" + "narasi-ahli-be/app/module/magazines/mapper" + "narasi-ahli-be/app/module/magazines/repository" + "narasi-ahli-be/app/module/magazines/request" + "narasi-ahli-be/app/module/magazines/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + config "narasi-ahli-be/config/config" + minioStorage "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/minio/minio-go/v7" + "github.com/rs/zerolog" +) + +// MagazinesService +type magazinesService struct { + Repo repository.MagazinesRepository + UsersRepo usersRepository.UsersRepository + MagazineFilesRepo magazineFilesRepository.MagazineFilesRepository + MinioStorage *minioStorage.MinioStorage + Log zerolog.Logger + Cfg *config.Config +} + +// MagazinesService define interface of IMagazinesService +type MagazinesService interface { + All(req request.MagazinesQueryRequest) (magazines []*response.MagazinesResponse, paging paginator.Pagination, err error) + Show(id uint) (magazines *response.MagazinesResponse, err error) + Save(req request.MagazinesCreateRequest, authToken string) (magazines *entity.Magazines, err error) + Update(id uint, req request.MagazinesUpdateRequest) (err error) + SaveThumbnail(c *fiber.Ctx) (err error) + Viewer(c *fiber.Ctx) (err error) + Delete(id uint) error +} + +// NewMagazinesService init MagazinesService +func NewMagazinesService(repo repository.MagazinesRepository, magazineFilesRepo magazineFilesRepository.MagazineFilesRepository, usersRepo usersRepository.UsersRepository, minioStorage *minioStorage.MinioStorage, log zerolog.Logger, cfg *config.Config) MagazinesService { + + return &magazinesService{ + Repo: repo, + MagazineFilesRepo: magazineFilesRepo, + UsersRepo: usersRepo, + MinioStorage: minioStorage, + Log: log, + Cfg: cfg, + } +} + +// All implement interface of MagazinesService +func (_i *magazinesService) All(req request.MagazinesQueryRequest) (magaziness []*response.MagazinesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + host := _i.Cfg.App.Domain + for _, result := range results { + magaziness = append(magaziness, mapper.MagazinesResponseMapper(result, _i.MagazineFilesRepo, host)) + } + + return +} + +func (_i *magazinesService) Show(id uint) (magazines *response.MagazinesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + host := _i.Cfg.App.Domain + return mapper.MagazinesResponseMapper(result, _i.MagazineFilesRepo, host), nil +} + +func (_i *magazinesService) Save(req request.MagazinesCreateRequest, authToken string) (magazines *entity.Magazines, err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + newReq.CreatedById = &createdBy.ID + + saveMagazineResponse, err := _i.Repo.Create(newReq) + if err != nil { + return nil, err + } + + return saveMagazineResponse, nil +} + +func (_i *magazinesService) SaveThumbnail(c *fiber.Ctx) (err error) { + + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:magazinesService", "Methods:SaveThumbnail"). + Interface("id", id).Msg("") + + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + + form, err := c.MultipartForm() + if err != nil { + return err + } + files := form.File["files"] + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + // Iterasi semua file yang diunggah + for _, file := range files { + + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "Uploader:: loop1"). + Interface("data", file).Msg("") + + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + filename := filepath.Base(file.Filename) + filename = strings.ReplaceAll(filename, " ", "") + filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))]) + extension := filepath.Ext(file.Filename)[1:] + + now := time.Now() + rand.New(rand.NewSource(now.UnixNano())) + randUniqueId := rand.Intn(1000000) + + newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId) + newFilename := newFilenameWithoutExt + "." + extension + objectName := fmt.Sprintf("magazines/thumbnail/%d/%d/%s", now.Year(), now.Month(), newFilename) + + findCategory, err := _i.Repo.FindOne(uint(id)) + findCategory.ThumbnailName = &newFilename + findCategory.ThumbnailPath = &objectName + err = _i.Repo.Update(uint(id), findCategory) + if err != nil { + return err + } + + // Upload file ke MinIO + _, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, file.Size, minio.PutObjectOptions{}) + if err != nil { + return err + } + } + + return +} + +func (_i *magazinesService) Update(id uint, req request.MagazinesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + entity := req.ToEntity() + + return _i.Repo.Update(id, entity) +} + +func (_i *magazinesService) Delete(id uint) error { + return _i.Repo.Delete(id) +} + +func (_i *magazinesService) Viewer(c *fiber.Ctx) (err error) { + thumbnailName := c.Params("thumbnailName") + + emptyImage := "empty-image.jpg" + searchThumbnail := emptyImage + if thumbnailName != emptyImage { + result, err := _i.Repo.FindByFilename(thumbnailName) + if err != nil { + return err + } + _i.Log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:Resource", "magazinesService:Viewer"). + Interface("resultThumbnail", result.ThumbnailPath).Msg("") + + if result.ThumbnailPath != nil { + searchThumbnail = *result.ThumbnailPath + } else { + searchThumbnail = "magazines/thumbnail/" + emptyImage + } + } else { + searchThumbnail = "magazines/thumbnail/" + emptyImage + } + + ctx := context.Background() + bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName + objectName := searchThumbnail + + // Create minio connection. + minioClient, err := _i.MinioStorage.ConnectMinio() + if err != nil { + // Return status 500 and minio connection error. + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": true, + "msg": err.Error(), + }) + } + + fileContent, err := minioClient.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{}) + if err != nil { + log.Fatalln(err) + } + defer fileContent.Close() + + contentType := mime.TypeByExtension("." + getFileExtension(objectName)) + if contentType == "" { + contentType = "application/octet-stream" + } + + c.Set("Content-Type", contentType) + + if _, err := io.Copy(c.Response().BodyWriter(), fileContent); err != nil { + return err + } + + return +} + +func getFileExtension(filename string) string { + // split file name + parts := strings.Split(filename, ".") + + // jika tidak ada ekstensi, kembalikan string kosong + if len(parts) == 1 || (len(parts) == 2 && parts[0] == "") { + return "" + } + + // ambil ekstensi terakhir + return parts[len(parts)-1] +} diff --git a/app/module/master_menus/controller/controller.go b/app/module/master_menus/controller/controller.go new file mode 100644 index 0000000..0e2b32b --- /dev/null +++ b/app/module/master_menus/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/master_menus/service" + +type Controller struct { + MasterMenus MasterMenusController +} + +func NewController(MasterMenusService service.MasterMenusService) *Controller { + return &Controller{ + MasterMenus: NewMasterMenusController(MasterMenusService), + } +} diff --git a/app/module/master_menus/controller/master_menus.controller.go b/app/module/master_menus/controller/master_menus.controller.go new file mode 100644 index 0000000..e4ba13a --- /dev/null +++ b/app/module/master_menus/controller/master_menus.controller.go @@ -0,0 +1,195 @@ +package controller + +import ( + "narasi-ahli-be/app/module/master_menus/request" + "narasi-ahli-be/app/module/master_menus/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type masterMenusController struct { + masterMenusService service.MasterMenusService +} + +type MasterMenusController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewMasterMenusController(masterMenusService service.MasterMenusService) MasterMenusController { + return &masterMenusController{ + masterMenusService: masterMenusService, + } +} + +// All MasterMenus +// @Summary Get all MasterMenus +// @Description API for getting all MasterMenus +// @Tags MasterMenus +// @Security Bearer +// @Param req query request.MasterMenusQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-menus [get] +func (_i *masterMenusController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.MasterMenusQueryRequestContext{ + Name: c.Query("name"), + Description: c.Query("description"), + ParentMenuId: c.Query("parentMenuId"), + ModuleId: c.Query("moduleId"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + masterMenusData, paging, err := _i.masterMenusService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterMenus list successfully retrieved"}, + Data: masterMenusData, + Meta: paging, + }) +} + +// Show get one MasterMenus +// @Summary Get one MasterMenus +// @Description API for getting one MasterMenus +// @Tags MasterMenus +// @Security Bearer +// @Param id path int true "MasterMenus ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-menus/{id} [get] +func (_i *masterMenusController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + masterMenusData, err := _i.masterMenusService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterMenus successfully retrieved"}, + Data: masterMenusData, + }) +} + +// Save create MasterMenus +// @Summary Create MasterMenus +// @Description API for create MasterMenus +// @Tags MasterMenus +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.MasterMenusCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-menus [post] +func (_i *masterMenusController) Save(c *fiber.Ctx) error { + req := new(request.MasterMenusCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.masterMenusService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterMenus successfully created"}, + }) +} + +// Update MasterMenus +// @Summary Update MasterMenus +// @Description API for update MasterMenus +// @Tags MasterMenus +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Body request.MasterMenusUpdateRequest +// @Param id path int true "MasterMenus ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /master-menus/{id} [put] +func (_i *masterMenusController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.MasterMenusUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.masterMenusService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterMenus successfully updated"}, + }) +} + +// Delete MasterMenus +// @Summary Delete MasterMenus +// @Description API for delete MasterMenus +// @Tags MasterMenus +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "MasterMenus ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-menus/{id} [delete] +func (_i *masterMenusController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.masterMenusService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterMenus successfully deleted"}, + }) +} diff --git a/app/module/master_menus/mapper/master_menus.mapper.go b/app/module/master_menus/mapper/master_menus.mapper.go new file mode 100644 index 0000000..6c7dc7b --- /dev/null +++ b/app/module/master_menus/mapper/master_menus.mapper.go @@ -0,0 +1,25 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/master_menus/response" +) + +func MasterMenusResponseMapper(masterMenusReq *entity.MasterMenus) (masterMenusRes *res.MasterMenusResponse) { + if masterMenusReq != nil { + masterMenusRes = &res.MasterMenusResponse{ + ID: masterMenusReq.ID, + Name: masterMenusReq.Name, + Description: masterMenusReq.Description, + ModuleId: masterMenusReq.ModuleId, + ParentMenuId: masterMenusReq.ParentMenuId, + Icon: masterMenusReq.Icon, + Position: masterMenusReq.Position, + StatusId: masterMenusReq.StatusId, + IsActive: masterMenusReq.IsActive, + CreatedAt: masterMenusReq.CreatedAt, + UpdatedAt: masterMenusReq.UpdatedAt, + } + } + return masterMenusRes +} diff --git a/app/module/master_menus/master_menus.module.go b/app/module/master_menus/master_menus.module.go new file mode 100644 index 0000000..e98ddfd --- /dev/null +++ b/app/module/master_menus/master_menus.module.go @@ -0,0 +1,54 @@ +package master_menus + +import ( + "narasi-ahli-be/app/module/master_menus/controller" + "narasi-ahli-be/app/module/master_menus/repository" + "narasi-ahli-be/app/module/master_menus/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of MasterMenusRouter +type MasterMenusRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of MasterMenus module +var NewMasterMenusModule = fx.Options( + // register repository of MasterMenus module + fx.Provide(repository.NewMasterMenusRepository), + + // register service of MasterMenus module + fx.Provide(service.NewMasterMenusService), + + // register controller of MasterMenus module + fx.Provide(controller.NewController), + + // register router of MasterMenus module + fx.Provide(NewMasterMenusRouter), +) + +// init MasterMenusRouter +func NewMasterMenusRouter(fiber *fiber.App, controller *controller.Controller) *MasterMenusRouter { + return &MasterMenusRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of MasterMenus module +func (_i *MasterMenusRouter) RegisterMasterMenusRoutes() { + // define controllers + masterMenusController := _i.Controller.MasterMenus + + // define routes + _i.App.Route("/master-menus", func(router fiber.Router) { + router.Get("/", masterMenusController.All) + router.Get("/:id", masterMenusController.Show) + router.Post("/", masterMenusController.Save) + router.Put("/:id", masterMenusController.Update) + router.Delete("/:id", masterMenusController.Delete) + }) +} diff --git a/app/module/master_menus/repository/master_menus.repository.go b/app/module/master_menus/repository/master_menus.repository.go new file mode 100644 index 0000000..8a8e84b --- /dev/null +++ b/app/module/master_menus/repository/master_menus.repository.go @@ -0,0 +1,108 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/master_menus/request" + "narasi-ahli-be/utils/paginator" + "strings" +) + +type masterMenusRepository struct { + DB *database.Database +} + +// MasterMenusRepository define interface of IMasterMenusRepository +type MasterMenusRepository interface { + GetAll(req request.MasterMenusQueryRequest) (masterMenuss []*entity.MasterMenus, paging paginator.Pagination, err error) + FindOne(id uint) (masterMenus *entity.MasterMenus, err error) + FindLastMenuPosition() (position *int, err error) + Create(masterMenus *entity.MasterMenus) (err error) + Update(id uint, masterMenus *entity.MasterMenus) (err error) + Delete(id uint) (err error) +} + +func NewMasterMenusRepository(db *database.Database) MasterMenusRepository { + return &masterMenusRepository{ + DB: db, + } +} + +// implement interface of IMasterMenusRepository +func (_i *masterMenusRepository) GetAll(req request.MasterMenusQueryRequest) (masterMenuss []*entity.MasterMenus, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.MasterMenus{}) + query = query.Where("is_active = ?", true) + + if req.Name != nil && *req.Name != "" { + name := strings.ToLower(*req.Name) + query = query.Where("LOWER(name) LIKE ?", "%"+strings.ToLower(name)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.ModuleId != nil { + query = query.Where("module_id = ?", req.ModuleId) + } + if req.ParentMenuId != nil { + query = query.Where("parent_menu_id = ?", req.ParentMenuId) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&masterMenuss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *masterMenusRepository) FindOne(id uint) (masterMenus *entity.MasterMenus, err error) { + if err := _i.DB.DB.First(&masterMenus, id).Error; err != nil { + return nil, err + } + + return masterMenus, nil +} + +func (_i *masterMenusRepository) FindLastMenuPosition() (position *int, err error) { + var masterMenus *entity.MasterMenus + if err := _i.DB.DB.Where("position IS NOT NULL").Order(fmt.Sprintf("%s %s", "position", "DESC")).First(&masterMenus).Error; err != nil { + return nil, err + } + + return masterMenus.Position, nil +} + +func (_i *masterMenusRepository) Create(masterMenus *entity.MasterMenus) (err error) { + return _i.DB.DB.Create(masterMenus).Error +} + +func (_i *masterMenusRepository) Update(id uint, masterMenus *entity.MasterMenus) (err error) { + return _i.DB.DB.Model(&entity.MasterMenus{}). + Where(&entity.MasterMenus{ID: id}). + Updates(masterMenus).Error +} + +func (_i *masterMenusRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.MasterMenus{}, id).Error +} diff --git a/app/module/master_menus/request/master_menus.request.go b/app/module/master_menus/request/master_menus.request.go new file mode 100644 index 0000000..924e118 --- /dev/null +++ b/app/module/master_menus/request/master_menus.request.go @@ -0,0 +1,108 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type MasterMenusGeneric interface { + ToEntity() +} + +type MasterMenusQueryRequest struct { + Name *string `json:"name"` + Description *string `json:"description"` + ModuleId *int `json:"moduleId"` + ParentMenuId *int `json:"parentMenuId"` + StatusId *int `json:"statusId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type MasterMenusCreateRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + ModuleId int `json:"moduleId" validate:"required"` + Group string `json:"group" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + ParentMenuId *int `json:"parentMenuId"` + Icon *string `json:"icon"` +} + +func (req MasterMenusCreateRequest) ToEntity() *entity.MasterMenus { + return &entity.MasterMenus{ + Name: req.Name, + Description: req.Description, + ModuleId: req.ModuleId, + ParentMenuId: req.ParentMenuId, + Icon: req.Icon, + Group: req.Group, + StatusId: req.StatusId, + } +} + +type MasterMenusUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + ModuleId int `json:"moduleId" validate:"required"` + Group string `json:"group" validate:"required"` + StatusId int `json:"statusId" validate:"required"` + ParentMenuId *int `json:"parentMenuId"` + Icon *string `json:"icon"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func (req MasterMenusUpdateRequest) ToEntity() *entity.MasterMenus { + return &entity.MasterMenus{ + ID: req.ID, + Name: req.Name, + Description: req.Description, + ModuleId: req.ModuleId, + ParentMenuId: req.ParentMenuId, + Icon: req.Icon, + Group: req.Group, + StatusId: req.StatusId, + UpdatedAt: time.Now(), + } +} + +type MasterMenusQueryRequestContext struct { + Name string `json:"name"` + Description string `json:"description"` + ModuleId string `json:"moduleId"` + ParentMenuId string `json:"parentMenuId"` + StatusId string `json:"statusId"` +} + +func (req MasterMenusQueryRequestContext) ToParamRequest() MasterMenusQueryRequest { + var request MasterMenusQueryRequest + + if name := req.Name; name != "" { + request.Name = &name + } + if description := req.Description; description != "" { + request.Description = &description + } + if moduleIdStr := req.ModuleId; moduleIdStr != "" { + moduleId, err := strconv.Atoi(moduleIdStr) + if err == nil { + request.ModuleId = &moduleId + } + } + if parentMenuIdStr := req.ParentMenuId; parentMenuIdStr != "" { + parentMenuId, err := strconv.Atoi(parentMenuIdStr) + if err == nil { + request.ParentMenuId = &parentMenuId + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/master_menus/response/master_menus.response.go b/app/module/master_menus/response/master_menus.response.go new file mode 100644 index 0000000..3f814df --- /dev/null +++ b/app/module/master_menus/response/master_menus.response.go @@ -0,0 +1,17 @@ +package response + +import "time" + +type MasterMenusResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + ModuleId int `json:"module_id"` + ParentMenuId *int `json:"parent_menu_id"` + Icon *string `json:"icon"` + Position *int `json:"position"` + StatusId int `json:"status_id"` + IsActive *bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/master_menus/service/master_menus.service.go b/app/module/master_menus/service/master_menus.service.go new file mode 100644 index 0000000..e85ec0d --- /dev/null +++ b/app/module/master_menus/service/master_menus.service.go @@ -0,0 +1,91 @@ +package service + +import ( + "narasi-ahli-be/app/module/master_menus/mapper" + "narasi-ahli-be/app/module/master_menus/repository" + "narasi-ahli-be/app/module/master_menus/request" + "narasi-ahli-be/app/module/master_menus/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// MasterMenusService +type masterMenusService struct { + Repo repository.MasterMenusRepository + Log zerolog.Logger +} + +// MasterMenusService define interface of IMasterMenusService +type MasterMenusService interface { + All(req request.MasterMenusQueryRequest) (masterMenus []*response.MasterMenusResponse, paging paginator.Pagination, err error) + Show(id uint) (masterMenus *response.MasterMenusResponse, err error) + Save(req request.MasterMenusCreateRequest) (err error) + Update(id uint, req request.MasterMenusUpdateRequest) (err error) + Delete(id uint) error +} + +// NewMasterMenusService init MasterMenusService +func NewMasterMenusService(repo repository.MasterMenusRepository, log zerolog.Logger) MasterMenusService { + + return &masterMenusService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of MasterMenusService +func (_i *masterMenusService) All(req request.MasterMenusQueryRequest) (masterMenuss []*response.MasterMenusResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + masterMenuss = append(masterMenuss, mapper.MasterMenusResponseMapper(result)) + } + + return +} + +func (_i *masterMenusService) Show(id uint) (masterMenus *response.MasterMenusResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.MasterMenusResponseMapper(result), nil +} + +func (_i *masterMenusService) Save(req request.MasterMenusCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + newReq := req.ToEntity() + var latestPosition, _ = _i.Repo.FindLastMenuPosition() + if err != nil { + return err + } + *latestPosition = *latestPosition + 1 + + if latestPosition != nil { + newReq.Position = latestPosition + } + + return _i.Repo.Create(newReq) +} + +func (_i *masterMenusService) Update(id uint, req request.MasterMenusUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *masterMenusService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} diff --git a/app/module/master_modules/controller/controller.go b/app/module/master_modules/controller/controller.go new file mode 100644 index 0000000..e82232e --- /dev/null +++ b/app/module/master_modules/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/master_modules/service" + +type Controller struct { + MasterModules MasterModulesController +} + +func NewController(MasterModulesService service.MasterModulesService) *Controller { + return &Controller{ + MasterModules: NewMasterModulesController(MasterModulesService), + } +} diff --git a/app/module/master_modules/controller/master_modules.controller.go b/app/module/master_modules/controller/master_modules.controller.go new file mode 100644 index 0000000..5a9805c --- /dev/null +++ b/app/module/master_modules/controller/master_modules.controller.go @@ -0,0 +1,192 @@ +package controller + +import ( + "narasi-ahli-be/app/module/master_modules/request" + "narasi-ahli-be/app/module/master_modules/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type masterModulesController struct { + masterModulesService service.MasterModulesService +} + +type MasterModulesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewMasterModulesController(masterModulesService service.MasterModulesService) MasterModulesController { + return &masterModulesController{ + masterModulesService: masterModulesService, + } +} + +// All MasterModules +// @Summary Get all MasterModules +// @Description API for getting all MasterModules +// @Tags MasterModules +// @Security Bearer +// @Param req query request.MasterModulesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-modules [get] +func (_i *masterModulesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.MasterModulesQueryRequestContext{ + Name: c.Query("name"), + Description: c.Query("description"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + masterModulesData, paging, err := _i.masterModulesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterModules list successfully retrieved"}, + Data: masterModulesData, + Meta: paging, + }) +} + +// Show MasterModules +// @Summary Get one MasterModules +// @Description API for getting one MasterModules +// @Tags MasterModules +// @Security Bearer +// @Param id path int true "MasterModules ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-modules/{id} [get] +func (_i *masterModulesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + masterModulesData, err := _i.masterModulesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterModules successfully retrieved"}, + Data: masterModulesData, + }) +} + +// Save MasterModules +// @Summary Create MasterModules +// @Description API for create MasterModules +// @Tags MasterModules +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.MasterModulesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-modules [post] +func (_i *masterModulesController) Save(c *fiber.Ctx) error { + req := new(request.MasterModulesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.masterModulesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterModules successfully created"}, + }) +} + +// Update MasterModules +// @Summary Update MasterModules +// @Description API for update MasterModules +// @Tags MasterModules +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "MasterModules ID" +// @Param payload body request.MasterModulesUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-modules/{id} [put] +func (_i *masterModulesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.MasterModulesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.masterModulesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterModules successfully updated"}, + }) +} + +// Delete MasterModules +// @Summary Delete MasterModules +// @Description API for delete MasterModules +// @Tags MasterModules +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "MasterModules ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /master-modules/{id} [delete] +func (_i *masterModulesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.masterModulesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MasterModules successfully deleted"}, + }) +} diff --git a/app/module/master_modules/mapper/master_modules.mapper.go b/app/module/master_modules/mapper/master_modules.mapper.go new file mode 100644 index 0000000..2e22fc7 --- /dev/null +++ b/app/module/master_modules/mapper/master_modules.mapper.go @@ -0,0 +1,22 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/master_modules/response" +) + +func MasterModulesResponseMapper(masterModulesReq *entity.MasterModules) (masterModulesRes *res.MasterModulesResponse) { + if masterModulesReq != nil { + masterModulesRes = &res.MasterModulesResponse{ + ID: masterModulesReq.ID, + Name: masterModulesReq.Name, + Description: masterModulesReq.Description, + PathUrl: masterModulesReq.PathUrl, + StatusId: masterModulesReq.StatusId, + IsActive: masterModulesReq.IsActive, + CreatedAt: masterModulesReq.CreatedAt, + UpdatedAt: masterModulesReq.UpdatedAt, + } + } + return masterModulesRes +} diff --git a/app/module/master_modules/master_modules.module.go b/app/module/master_modules/master_modules.module.go new file mode 100644 index 0000000..7f0e479 --- /dev/null +++ b/app/module/master_modules/master_modules.module.go @@ -0,0 +1,54 @@ +package master_modules + +import ( + "narasi-ahli-be/app/module/master_modules/controller" + "narasi-ahli-be/app/module/master_modules/repository" + "narasi-ahli-be/app/module/master_modules/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of MasterModulesRouter +type MasterModulesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of MasterModules module +var NewMasterModulesModule = fx.Options( + // register repository of MasterModules module + fx.Provide(repository.NewMasterModulesRepository), + + // register service of MasterModules module + fx.Provide(service.NewMasterModulesService), + + // register controller of MasterModules module + fx.Provide(controller.NewController), + + // register router of MasterModules module + fx.Provide(NewMasterModulesRouter), +) + +// init MasterModulesRouter +func NewMasterModulesRouter(fiber *fiber.App, controller *controller.Controller) *MasterModulesRouter { + return &MasterModulesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of MasterModules module +func (_i *MasterModulesRouter) RegisterMasterModulesRoutes() { + // define controllers + masterModulesController := _i.Controller.MasterModules + + // define routes + _i.App.Route("/master-modules", func(router fiber.Router) { + router.Get("/", masterModulesController.All) + router.Get("/:id", masterModulesController.Show) + router.Post("/", masterModulesController.Save) + router.Put("/:id", masterModulesController.Update) + router.Delete("/:id", masterModulesController.Delete) + }) +} diff --git a/app/module/master_modules/repository/master_modules.repository.go b/app/module/master_modules/repository/master_modules.repository.go new file mode 100644 index 0000000..f2ecfa8 --- /dev/null +++ b/app/module/master_modules/repository/master_modules.repository.go @@ -0,0 +1,92 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/master_modules/request" + "narasi-ahli-be/utils/paginator" + "strings" +) + +type masterModulesRepository struct { + DB *database.Database +} + +// MasterModulesRepository define interface of IMasterModulesRepository +type MasterModulesRepository interface { + GetAll(req request.MasterModulesQueryRequest) (masterModuless []*entity.MasterModules, paging paginator.Pagination, err error) + FindOne(id uint) (masterModules *entity.MasterModules, err error) + Create(masterModules *entity.MasterModules) (err error) + Update(id uint, masterModules *entity.MasterModules) (err error) + Delete(id uint) (err error) +} + +func NewMasterModulesRepository(db *database.Database) MasterModulesRepository { + return &masterModulesRepository{ + DB: db, + } +} + +// implement interface of IMasterModulesRepository +func (_i *masterModulesRepository) GetAll(req request.MasterModulesQueryRequest) (masterModuless []*entity.MasterModules, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.MasterModules{}) + query = query.Where("is_active = ?", true) + + if req.Name != nil && *req.Name != "" { + name := strings.ToLower(*req.Name) + query = query.Where("LOWER(name) LIKE ?", "%"+strings.ToLower(name)+"%") + } + if req.Description != nil && *req.Description != "" { + description := strings.ToLower(*req.Description) + query = query.Where("LOWER(description) LIKE ?", "%"+strings.ToLower(description)+"%") + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&masterModuless).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *masterModulesRepository) FindOne(id uint) (masterModules *entity.MasterModules, err error) { + if err := _i.DB.DB.First(&masterModules, id).Error; err != nil { + return nil, err + } + + return masterModules, nil +} + +func (_i *masterModulesRepository) Create(masterModules *entity.MasterModules) (err error) { + return _i.DB.DB.Create(masterModules).Error +} + +func (_i *masterModulesRepository) Update(id uint, masterModules *entity.MasterModules) (err error) { + return _i.DB.DB.Model(&entity.MasterModules{}). + Where(&entity.MasterModules{ID: id}). + Updates(masterModules).Error +} + +func (_i *masterModulesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.MasterModules{}, id).Error +} diff --git a/app/module/master_modules/request/master_modules.request.go b/app/module/master_modules/request/master_modules.request.go new file mode 100644 index 0000000..f70328b --- /dev/null +++ b/app/module/master_modules/request/master_modules.request.go @@ -0,0 +1,79 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type MasterModulesGeneric interface { + ToEntity() +} + +type MasterModulesQueryRequest struct { + Name *string `json:"name"` + Description *string `json:"description"` + StatusId *int `json:"statusId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type MasterModulesCreateRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + PathUrl string `json:"pathUrl" validate:"required"` + StatusId int `json:"statusId" validate:"required"` +} + +func (req MasterModulesCreateRequest) ToEntity() *entity.MasterModules { + return &entity.MasterModules{ + Name: req.Name, + Description: req.Description, + PathUrl: req.PathUrl, + StatusId: req.StatusId, + } +} + +type MasterModulesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + PathUrl string `json:"pathUrl" validate:"required"` + StatusId int `json:"statusId" validate:"required"` +} + +func (req MasterModulesUpdateRequest) ToEntity() *entity.MasterModules { + return &entity.MasterModules{ + ID: req.ID, + Name: req.Name, + Description: req.Description, + PathUrl: req.PathUrl, + StatusId: req.StatusId, + UpdatedAt: time.Now(), + } +} + +type MasterModulesQueryRequestContext struct { + Name string `json:"name"` + Description string `json:"description"` + StatusId string `json:"statusId"` +} + +func (req MasterModulesQueryRequestContext) ToParamRequest() MasterModulesQueryRequest { + var request MasterModulesQueryRequest + + if name := req.Name; name != "" { + request.Name = &name + } + if description := req.Description; description != "" { + request.Description = &description + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/master_modules/response/master_modules.response.go b/app/module/master_modules/response/master_modules.response.go new file mode 100644 index 0000000..9a80bf0 --- /dev/null +++ b/app/module/master_modules/response/master_modules.response.go @@ -0,0 +1,14 @@ +package response + +import "time" + +type MasterModulesResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + PathUrl string `json:"path_url"` + StatusId int `json:"status_id"` + IsActive *bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/master_modules/service/master_modules.service.go b/app/module/master_modules/service/master_modules.service.go new file mode 100644 index 0000000..7afbed3 --- /dev/null +++ b/app/module/master_modules/service/master_modules.service.go @@ -0,0 +1,80 @@ +package service + +import ( + "narasi-ahli-be/app/module/master_modules/mapper" + "narasi-ahli-be/app/module/master_modules/repository" + "narasi-ahli-be/app/module/master_modules/request" + "narasi-ahli-be/app/module/master_modules/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// MasterModulesService +type masterModulesService struct { + Repo repository.MasterModulesRepository + Log zerolog.Logger +} + +// MasterModulesService define interface of IMasterModulesService +type MasterModulesService interface { + All(req request.MasterModulesQueryRequest) (masterModules []*response.MasterModulesResponse, paging paginator.Pagination, err error) + Show(id uint) (masterModules *response.MasterModulesResponse, err error) + Save(req request.MasterModulesCreateRequest) (err error) + Update(id uint, req request.MasterModulesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewMasterModulesService init MasterModulesService +func NewMasterModulesService(repo repository.MasterModulesRepository, log zerolog.Logger) MasterModulesService { + + return &masterModulesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of MasterModulesService +func (_i *masterModulesService) All(req request.MasterModulesQueryRequest) (masterModuless []*response.MasterModulesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + masterModuless = append(masterModuless, mapper.MasterModulesResponseMapper(result)) + } + + return +} + +func (_i *masterModulesService) Show(id uint) (masterModules *response.MasterModulesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.MasterModulesResponseMapper(result), nil +} + +func (_i *masterModulesService) Save(req request.MasterModulesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *masterModulesService) Update(id uint, req request.MasterModulesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *masterModulesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} diff --git a/app/module/master_statuses/controller/controller.go b/app/module/master_statuses/controller/controller.go new file mode 100644 index 0000000..e607bfd --- /dev/null +++ b/app/module/master_statuses/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/master_statuses/service" + +type Controller struct { + MasterStatuses MasterStatusesController +} + +func NewController(MasterStatusesService service.MasterStatusesService) *Controller { + return &Controller{ + MasterStatuses: NewMasterStatusesController(MasterStatusesService), + } +} diff --git a/app/module/master_statuses/controller/master_statuses.controller.go b/app/module/master_statuses/controller/master_statuses.controller.go new file mode 100644 index 0000000..b415b27 --- /dev/null +++ b/app/module/master_statuses/controller/master_statuses.controller.go @@ -0,0 +1,182 @@ +package controller + +import ( + "narasi-ahli-be/app/module/master_statuses/request" + "narasi-ahli-be/app/module/master_statuses/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type masterStatusesController struct { + masterStatusesService service.MasterStatusesService +} + +type MasterStatusesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewMasterStatusesController(masterStatusesService service.MasterStatusesService) MasterStatusesController { + return &masterStatusesController{ + masterStatusesService: masterStatusesService, + } +} + +// All MasterStatuses +// @Summary Get all MasterStatuses +// @Description API for getting all MasterStatuses +// @Tags Untags +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /master-statuses [get] +func (_i *masterStatusesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + var req request.MasterStatusesQueryRequest + req.Pagination = paginate + + masterStatusesData, paging, err := _i.masterStatusesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MasterStatuses list successfully retrieved"}, + Data: masterStatusesData, + Meta: paging, + }) +} + +// Show MasterStatuses +// @Summary Get one MasterStatuses +// @Description API for getting one MasterStatuses +// @Tags Untags +// @Security Bearer +// @Param id path int true "MasterStatuses ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /master-statuses/{id} [get] +func (_i *masterStatusesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + masterStatusesData, err := _i.masterStatusesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MasterStatuses successfully retrieved"}, + Data: masterStatusesData, + }) +} + +// Save MasterStatuses +// @Summary Create MasterStatuses +// @Description API for create MasterStatuses +// @Tags Untags +// @Security Bearer +// @Body request.MasterStatusesCreateRequest +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /master-statuses [post] +func (_i *masterStatusesController) Save(c *fiber.Ctx) error { + req := new(request.MasterStatusesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.masterStatusesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MasterStatuses successfully created"}, + }) +} + +// Update MasterStatuses +// @Summary Update MasterStatuses +// @Description API for update MasterStatuses +// @Tags Untags +// @Security Bearer +// @Body request.MasterStatusesUpdateRequest +// @Param id path int true "MasterStatuses ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /master-statuses/{id} [put] +func (_i *masterStatusesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.MasterStatusesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.masterStatusesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MasterStatuses successfully updated"}, + }) +} + +// Delete MasterStatuses +// @Summary Delete MasterStatuses +// @Description API for delete MasterStatuses +// @Tags Untags +// @Security Bearer +// @Param id path int true "MasterStatuses ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /master-statuses/{id} [delete] +func (_i *masterStatusesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.masterStatusesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"MasterStatuses successfully deleted"}, + }) +} diff --git a/app/module/master_statuses/mapper/master_statuses.mapper.go b/app/module/master_statuses/mapper/master_statuses.mapper.go new file mode 100644 index 0000000..b0fb539 --- /dev/null +++ b/app/module/master_statuses/mapper/master_statuses.mapper.go @@ -0,0 +1,17 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/master_statuses/response" +) + +func MasterStatusesResponseMapper(masterStatusesReq *entity.MasterStatuses) (masterStatusesRes *res.MasterStatusesResponse) { + if masterStatusesReq != nil { + masterStatusesRes = &res.MasterStatusesResponse{ + ID: masterStatusesReq.ID, + Name: masterStatusesReq.Name, + IsActive: masterStatusesReq.IsActive, + } + } + return masterStatusesRes +} diff --git a/app/module/master_statuses/master_statuses.module.go b/app/module/master_statuses/master_statuses.module.go new file mode 100644 index 0000000..3d19d75 --- /dev/null +++ b/app/module/master_statuses/master_statuses.module.go @@ -0,0 +1,54 @@ +package master_statuses + +import ( + "narasi-ahli-be/app/module/master_statuses/controller" + "narasi-ahli-be/app/module/master_statuses/repository" + "narasi-ahli-be/app/module/master_statuses/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of MasterStatusesRouter +type MasterStatusesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of MasterStatuses module +var NewMasterStatusesModule = fx.Options( + // register repository of MasterStatuses module + fx.Provide(repository.NewMasterStatusesRepository), + + // register service of MasterStatuses module + fx.Provide(service.NewMasterStatusesService), + + // register controller of MasterStatuses module + fx.Provide(controller.NewController), + + // register router of MasterStatuses module + fx.Provide(NewMasterStatusesRouter), +) + +// init MasterStatusesRouter +func NewMasterStatusesRouter(fiber *fiber.App, controller *controller.Controller) *MasterStatusesRouter { + return &MasterStatusesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of MasterStatuses module +func (_i *MasterStatusesRouter) RegisterMasterStatusesRoutes() { + // define controllers + masterStatusesController := _i.Controller.MasterStatuses + + // define routes + _i.App.Route("/master-statuses", func(router fiber.Router) { + router.Get("/", masterStatusesController.All) + router.Get("/:id", masterStatusesController.Show) + router.Post("/", masterStatusesController.Save) + router.Put("/:id", masterStatusesController.Update) + router.Delete("/:id", masterStatusesController.Delete) + }) +} diff --git a/app/module/master_statuses/repository/master_statuses.repository.go b/app/module/master_statuses/repository/master_statuses.repository.go new file mode 100644 index 0000000..a211cc2 --- /dev/null +++ b/app/module/master_statuses/repository/master_statuses.repository.go @@ -0,0 +1,69 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/master_statuses/request" + "narasi-ahli-be/utils/paginator" +) + +type masterStatusesRepository struct { + DB *database.Database +} + +// MasterStatusesRepository define interface of IMasterStatusesRepository +type MasterStatusesRepository interface { + GetAll(req request.MasterStatusesQueryRequest) (masterStatusess []*entity.MasterStatuses, paging paginator.Pagination, err error) + FindOne(id uint) (masterStatuses *entity.MasterStatuses, err error) + Create(masterStatuses *entity.MasterStatuses) (err error) + Update(id uint, masterStatuses *entity.MasterStatuses) (err error) + Delete(id uint) (err error) +} + +func NewMasterStatusesRepository(db *database.Database) MasterStatusesRepository { + return &masterStatusesRepository{ + DB: db, + } +} + +// implement interface of IMasterStatusesRepository +func (_i *masterStatusesRepository) GetAll(req request.MasterStatusesQueryRequest) (masterStatusess []*entity.MasterStatuses, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.MasterStatuses{}) + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&masterStatusess).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *masterStatusesRepository) FindOne(id uint) (masterStatuses *entity.MasterStatuses, err error) { + if err := _i.DB.DB.First(&masterStatuses, id).Error; err != nil { + return nil, err + } + + return masterStatuses, nil +} + +func (_i *masterStatusesRepository) Create(masterStatuses *entity.MasterStatuses) (err error) { + return _i.DB.DB.Create(masterStatuses).Error +} + +func (_i *masterStatusesRepository) Update(id uint, masterStatuses *entity.MasterStatuses) (err error) { + return _i.DB.DB.Model(&entity.MasterStatuses{}). + Where(&entity.MasterStatuses{ID: id}). + Updates(masterStatuses).Error +} + +func (_i *masterStatusesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.MasterStatuses{}, id).Error +} diff --git a/app/module/master_statuses/request/master_statuses.request.go b/app/module/master_statuses/request/master_statuses.request.go new file mode 100644 index 0000000..d7489dc --- /dev/null +++ b/app/module/master_statuses/request/master_statuses.request.go @@ -0,0 +1,42 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" +) + +type MasterStatusesGeneric interface { + ToEntity() +} + +type MasterStatusesQueryRequest struct { + Name string `json:"name" validate:"required"` + IsActive bool `json:"is_active" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type MasterStatusesCreateRequest struct { + Name string `json:"name" validate:"required"` + IsActive bool `json:"is_active" validate:"required"` +} + +func (req MasterStatusesCreateRequest) ToEntity() *entity.MasterStatuses { + return &entity.MasterStatuses{ + Name: req.Name, + IsActive: req.IsActive, + } +} + +type MasterStatusesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + IsActive bool `json:"is_active" validate:"required"` +} + +func (req MasterStatusesUpdateRequest) ToEntity() *entity.MasterStatuses { + return &entity.MasterStatuses{ + ID: req.ID, + Name: req.Name, + IsActive: req.IsActive, + } +} diff --git a/app/module/master_statuses/response/master_statuses.response.go b/app/module/master_statuses/response/master_statuses.response.go new file mode 100644 index 0000000..51f5765 --- /dev/null +++ b/app/module/master_statuses/response/master_statuses.response.go @@ -0,0 +1,7 @@ +package response + +type MasterStatusesResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + IsActive bool `json:"is_active"` +} \ No newline at end of file diff --git a/app/module/master_statuses/service/master_statuses.service.go b/app/module/master_statuses/service/master_statuses.service.go new file mode 100644 index 0000000..198c72e --- /dev/null +++ b/app/module/master_statuses/service/master_statuses.service.go @@ -0,0 +1,73 @@ +package service + +import ( + "narasi-ahli-be/app/module/master_statuses/mapper" + "narasi-ahli-be/app/module/master_statuses/repository" + "narasi-ahli-be/app/module/master_statuses/request" + "narasi-ahli-be/app/module/master_statuses/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// MasterStatusesService +type masterStatusesService struct { + Repo repository.MasterStatusesRepository + Log zerolog.Logger +} + +// MasterStatusesService define interface of IMasterStatusesService +type MasterStatusesService interface { + All(req request.MasterStatusesQueryRequest) (masterStatuses []*response.MasterStatusesResponse, paging paginator.Pagination, err error) + Show(id uint) (masterStatuses *response.MasterStatusesResponse, err error) + Save(req request.MasterStatusesCreateRequest) (err error) + Update(id uint, req request.MasterStatusesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewMasterStatusesService init MasterStatusesService +func NewMasterStatusesService(repo repository.MasterStatusesRepository, log zerolog.Logger) MasterStatusesService { + + return &masterStatusesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of MasterStatusesService +func (_i *masterStatusesService) All(req request.MasterStatusesQueryRequest) (masterStatusess []*response.MasterStatusesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + masterStatusess = append(masterStatusess, mapper.MasterStatusesResponseMapper(result)) + } + + return +} + +func (_i *masterStatusesService) Show(id uint) (masterStatuses *response.MasterStatusesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.MasterStatusesResponseMapper(result), nil +} + +func (_i *masterStatusesService) Save(req request.MasterStatusesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *masterStatusesService) Update(id uint, req request.MasterStatusesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *masterStatusesService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/provinces/controller/controller.go b/app/module/provinces/controller/controller.go new file mode 100644 index 0000000..f3f4394 --- /dev/null +++ b/app/module/provinces/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/provinces/service" + +type Controller struct { + Provinces ProvincesController +} + +func NewController(ProvincesService service.ProvincesService) *Controller { + return &Controller{ + Provinces: NewProvincesController(ProvincesService), + } +} diff --git a/app/module/provinces/controller/provinces.controller.go b/app/module/provinces/controller/provinces.controller.go new file mode 100644 index 0000000..2b41c5c --- /dev/null +++ b/app/module/provinces/controller/provinces.controller.go @@ -0,0 +1,182 @@ +package controller + +import ( + "narasi-ahli-be/app/module/provinces/request" + "narasi-ahli-be/app/module/provinces/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type provincesController struct { + provincesService service.ProvincesService +} + +type ProvincesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewProvincesController(provincesService service.ProvincesService) ProvincesController { + return &provincesController{ + provincesService: provincesService, + } +} + +// All Provinces +// @Summary Get all Provinces +// @Description API for getting all Provinces +// @Tags Untags +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /provinces [get] +func (_i *provincesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + var req request.ProvincesQueryRequest + req.Pagination = paginate + + provincesData, paging, err := _i.provincesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Provinces list successfully retrieved"}, + Data: provincesData, + Meta: paging, + }) +} + +// Show Provinces +// @Summary Get one Provinces +// @Description API for getting one Provinces +// @Tags Untags +// @Security Bearer +// @Param id path int true "Provinces ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /provinces/{id} [get] +func (_i *provincesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + provincesData, err := _i.provincesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Provinces successfully retrieved"}, + Data: provincesData, + }) +} + +// Save Provinces +// @Summary Create Provinces +// @Description API for create Provinces +// @Tags Untags +// @Security Bearer +// @Body request.ProvincesCreateRequest +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /provinces [post] +func (_i *provincesController) Save(c *fiber.Ctx) error { + req := new(request.ProvincesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.provincesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Provinces successfully created"}, + }) +} + +// Update Provinces +// @Summary Update Provinces +// @Description API for update Provinces +// @Tags Untags +// @Security Bearer +// @Body request.ProvincesUpdateRequest +// @Param id path int true "Provinces ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /provinces/{id} [put] +func (_i *provincesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ProvincesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.provincesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Provinces successfully updated"}, + }) +} + +// Delete Provinces +// @Summary Delete Provinces +// @Description API for delete Provinces +// @Tags Untags +// @Security Bearer +// @Param id path int true "Provinces ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /provinces/{id} [delete] +func (_i *provincesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.provincesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"Provinces successfully deleted"}, + }) +} diff --git a/app/module/provinces/mapper/provinces.mapper.go b/app/module/provinces/mapper/provinces.mapper.go new file mode 100644 index 0000000..d7380a3 --- /dev/null +++ b/app/module/provinces/mapper/provinces.mapper.go @@ -0,0 +1,19 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/provinces/response" +) + +func ProvincesResponseMapper(provincesReq *entity.Provinces) (provincesRes *res.ProvincesResponse) { + if provincesReq != nil { + provincesRes = &res.ProvincesResponse{ + ID: provincesReq.ID, + ProvName: provincesReq.ProvName, + LocationId: provincesReq.LocationId, + Status: provincesReq.Status, + Timezone: provincesReq.Timezone, + } + } + return provincesRes +} diff --git a/app/module/provinces/provinces.module.go b/app/module/provinces/provinces.module.go new file mode 100644 index 0000000..8ac26a9 --- /dev/null +++ b/app/module/provinces/provinces.module.go @@ -0,0 +1,54 @@ +package provinces + +import ( + "narasi-ahli-be/app/module/provinces/controller" + "narasi-ahli-be/app/module/provinces/repository" + "narasi-ahli-be/app/module/provinces/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ProvincesRouter +type ProvincesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Provinces module +var NewProvincesModule = fx.Options( + // register repository of Provinces module + fx.Provide(repository.NewProvincesRepository), + + // register service of Provinces module + fx.Provide(service.NewProvincesService), + + // register controller of Provinces module + fx.Provide(controller.NewController), + + // register router of Provinces module + fx.Provide(NewProvincesRouter), +) + +// init ProvincesRouter +func NewProvincesRouter(fiber *fiber.App, controller *controller.Controller) *ProvincesRouter { + return &ProvincesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Provinces module +func (_i *ProvincesRouter) RegisterProvincesRoutes() { + // define controllers + provincesController := _i.Controller.Provinces + + // define routes + _i.App.Route("/provinces", func(router fiber.Router) { + router.Get("/", provincesController.All) + router.Get("/:id", provincesController.Show) + router.Post("/", provincesController.Save) + router.Put("/:id", provincesController.Update) + router.Delete("/:id", provincesController.Delete) + }) +} diff --git a/app/module/provinces/repository/provinces.repository.go b/app/module/provinces/repository/provinces.repository.go new file mode 100644 index 0000000..4889218 --- /dev/null +++ b/app/module/provinces/repository/provinces.repository.go @@ -0,0 +1,69 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/provinces/request" + "narasi-ahli-be/utils/paginator" +) + +type provincesRepository struct { + DB *database.Database +} + +// ProvincesRepository define interface of IProvincesRepository +type ProvincesRepository interface { + GetAll(req request.ProvincesQueryRequest) (provincess []*entity.Provinces, paging paginator.Pagination, err error) + FindOne(id uint) (provinces *entity.Provinces, err error) + Create(provinces *entity.Provinces) (err error) + Update(id uint, provinces *entity.Provinces) (err error) + Delete(id uint) (err error) +} + +func NewProvincesRepository(db *database.Database) ProvincesRepository { + return &provincesRepository{ + DB: db, + } +} + +// implement interface of IProvincesRepository +func (_i *provincesRepository) GetAll(req request.ProvincesQueryRequest) (provincess []*entity.Provinces, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Provinces{}) + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&provincess).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *provincesRepository) FindOne(id uint) (provinces *entity.Provinces, err error) { + if err := _i.DB.DB.First(&provinces, id).Error; err != nil { + return nil, err + } + + return provinces, nil +} + +func (_i *provincesRepository) Create(provinces *entity.Provinces) (err error) { + return _i.DB.DB.Create(provinces).Error +} + +func (_i *provincesRepository) Update(id uint, provinces *entity.Provinces) (err error) { + return _i.DB.DB.Model(&entity.Provinces{}). + Where(&entity.Provinces{ID: id}). + Updates(provinces).Error +} + +func (_i *provincesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.Provinces{}, id).Error +} diff --git a/app/module/provinces/request/provinces.request.go b/app/module/provinces/request/provinces.request.go new file mode 100644 index 0000000..f50d2f1 --- /dev/null +++ b/app/module/provinces/request/provinces.request.go @@ -0,0 +1,52 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" +) + +type ProvincesGeneric interface { + ToEntity() +} + +type ProvincesQueryRequest struct { + ProvName string `json:"prov_name" validate:"required"` + LocationId int `json:"location_id" validate:"required"` + Status int `json:"status" validate:"required"` + Timezone string `json:"timezone" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ProvincesCreateRequest struct { + ProvName string `json:"prov_name" validate:"required"` + LocationId int `json:"location_id" validate:"required"` + Status int `json:"status" validate:"required"` + Timezone string `json:"timezone" validate:"required"` +} + +func (req ProvincesCreateRequest) ToEntity() *entity.Provinces { + return &entity.Provinces{ + ProvName: req.ProvName, + LocationId: req.LocationId, + Status: req.Status, + Timezone: req.Timezone, + } +} + +type ProvincesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + ProvName string `json:"prov_name" validate:"required"` + LocationId int `json:"location_id" validate:"required"` + Status int `json:"status" validate:"required"` + Timezone string `json:"timezone" validate:"required"` +} + +func (req ProvincesUpdateRequest) ToEntity() *entity.Provinces { + return &entity.Provinces{ + ID: req.ID, + ProvName: req.ProvName, + LocationId: req.LocationId, + Status: req.Status, + Timezone: req.Timezone, + } +} diff --git a/app/module/provinces/response/provinces.response.go b/app/module/provinces/response/provinces.response.go new file mode 100644 index 0000000..0bfb39a --- /dev/null +++ b/app/module/provinces/response/provinces.response.go @@ -0,0 +1,9 @@ +package response + +type ProvincesResponse struct { + ID uint `json:"id"` + ProvName string `json:"prov_name"` + LocationId int `json:"location_id"` + Status int `json:"status"` + Timezone string `json:"timezone"` +} diff --git a/app/module/provinces/service/provinces.service.go b/app/module/provinces/service/provinces.service.go new file mode 100644 index 0000000..fcabaf0 --- /dev/null +++ b/app/module/provinces/service/provinces.service.go @@ -0,0 +1,73 @@ +package service + +import ( + "narasi-ahli-be/app/module/provinces/mapper" + "narasi-ahli-be/app/module/provinces/repository" + "narasi-ahli-be/app/module/provinces/request" + "narasi-ahli-be/app/module/provinces/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// ProvincesService +type provincesService struct { + Repo repository.ProvincesRepository + Log zerolog.Logger +} + +// ProvincesService define interface of IProvincesService +type ProvincesService interface { + All(req request.ProvincesQueryRequest) (provinces []*response.ProvincesResponse, paging paginator.Pagination, err error) + Show(id uint) (provinces *response.ProvincesResponse, err error) + Save(req request.ProvincesCreateRequest) (err error) + Update(id uint, req request.ProvincesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewProvincesService init ProvincesService +func NewProvincesService(repo repository.ProvincesRepository, log zerolog.Logger) ProvincesService { + + return &provincesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of ProvincesService +func (_i *provincesService) All(req request.ProvincesQueryRequest) (provincess []*response.ProvincesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + provincess = append(provincess, mapper.ProvincesResponseMapper(result)) + } + + return +} + +func (_i *provincesService) Show(id uint) (provinces *response.ProvincesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.ProvincesResponseMapper(result), nil +} + +func (_i *provincesService) Save(req request.ProvincesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *provincesService) Update(id uint, req request.ProvincesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *provincesService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/research_journals/controller/controller.go b/app/module/research_journals/controller/controller.go new file mode 100644 index 0000000..99b1b53 --- /dev/null +++ b/app/module/research_journals/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/research_journals/service" + +type Controller struct { + ResearchJournals ResearchJournalsController +} + +func NewController(ResearchJournalsService service.ResearchJournalsService) *Controller { + return &Controller{ + ResearchJournals: NewResearchJournalsController(ResearchJournalsService), + } +} diff --git a/app/module/research_journals/controller/research_journals.controller.go b/app/module/research_journals/controller/research_journals.controller.go new file mode 100644 index 0000000..9f6a990 --- /dev/null +++ b/app/module/research_journals/controller/research_journals.controller.go @@ -0,0 +1,215 @@ +package controller + +import ( + "narasi-ahli-be/app/module/research_journals/request" + "narasi-ahli-be/app/module/research_journals/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type researchJournalsController struct { + researchJournalsService service.ResearchJournalsService +} + +type ResearchJournalsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewResearchJournalsController(researchJournalsService service.ResearchJournalsService) ResearchJournalsController { + return &researchJournalsController{ + researchJournalsService: researchJournalsService, + } +} + +// All Research Journals +// @Summary Get all Research Journals +// @Description API for getting all Research Journals for authenticated user +// @Tags Research Journals +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.ResearchJournalsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /research-journals [get] +func (_i *researchJournalsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + reqContext := request.ResearchJournalsQueryRequestContext{ + JournalTitle: c.Query("journalTitle"), + Publisher: c.Query("publisher"), + PublishedYear: c.Query("publishedYear"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + // Handle publishedYear filter + if publishedYearStr := c.Query("publishedYear"); publishedYearStr != "" { + if publishedYear, err := strconv.Atoi(publishedYearStr); err == nil { + req.PublishedYear = &publishedYear + } + } + + journalsData, paging, err := _i.researchJournalsService.All(authHeader, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Research journals list successfully retrieved"}, + Data: journalsData, + Meta: paging, + }) +} + +// Show Research Journal +// @Summary Get one Research Journal +// @Description API for getting one Research Journal +// @Tags Research Journals +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Research Journal ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /research-journals/{id} [get] +func (_i *researchJournalsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + journalData, err := _i.researchJournalsService.Show(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Research journal successfully retrieved"}, + Data: journalData, + }) +} + +// Save Research Journal +// @Summary Create Research Journal +// @Description API for create Research Journal +// @Tags Research Journals +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.ResearchJournalsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /research-journals [post] +func (_i *researchJournalsController) Save(c *fiber.Ctx) error { + req := new(request.ResearchJournalsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + dataResult, err := _i.researchJournalsService.Save(authHeader, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Research journal successfully created"}, + Data: dataResult, + }) +} + +// Update Research Journal +// @Summary Update Research Journal +// @Description API for update Research Journal +// @Tags Research Journals +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Research Journal ID" +// @Param payload body request.ResearchJournalsUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /research-journals/{id} [put] +func (_i *researchJournalsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.ResearchJournalsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.researchJournalsService.Update(authHeader, uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Research journal successfully updated"}, + }) +} + +// Delete Research Journal +// @Summary Delete Research Journal +// @Description API for delete Research Journal +// @Tags Research Journals +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Research Journal ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /research-journals/{id} [delete] +func (_i *researchJournalsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.researchJournalsService.Delete(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Research journal successfully deleted"}, + }) +} diff --git a/app/module/research_journals/mapper/research_journals.mapper.go b/app/module/research_journals/mapper/research_journals.mapper.go new file mode 100644 index 0000000..86dbe26 --- /dev/null +++ b/app/module/research_journals/mapper/research_journals.mapper.go @@ -0,0 +1,36 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/research_journals/response" +) + +func ResearchJournalsResponseMapper(researchJournal *entity.ResearchJournals) *response.ResearchJournalsResponse { + result := &response.ResearchJournalsResponse{ + ID: researchJournal.ID, + UserID: researchJournal.UserID, + JournalTitle: researchJournal.JournalTitle, + Publisher: researchJournal.Publisher, + JournalURL: researchJournal.JournalURL, + PublishedDate: researchJournal.PublishedDate, + CreatedAt: researchJournal.CreatedAt, + UpdatedAt: researchJournal.UpdatedAt, + } + + // Extract year from PublishedDate + if researchJournal.PublishedDate != nil { + year := researchJournal.PublishedDate.Year() + result.PublishedYear = &year + } + + if researchJournal.User != nil { + result.User = &response.UserBasicInfo{ + ID: researchJournal.User.ID, + Username: researchJournal.User.Username, + Fullname: researchJournal.User.Fullname, + Email: researchJournal.User.Email, + } + } + + return result +} diff --git a/app/module/research_journals/repository/research_journals.repository.go b/app/module/research_journals/repository/research_journals.repository.go new file mode 100644 index 0000000..2fcd4db --- /dev/null +++ b/app/module/research_journals/repository/research_journals.repository.go @@ -0,0 +1,84 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/research_journals/request" + "narasi-ahli-be/utils/paginator" +) + +type researchJournalsRepository struct { + DB *database.Database +} + +type ResearchJournalsRepository interface { + GetAll(userId uint, req request.ResearchJournalsQueryRequest) (researchJournals []*entity.ResearchJournals, paging paginator.Pagination, err error) + FindOneByUserAndId(userId uint, id uint) (researchJournal *entity.ResearchJournals, err error) + Create(researchJournal *entity.ResearchJournals) (result *entity.ResearchJournals, err error) + Update(userId uint, id uint, researchJournal *entity.ResearchJournals) (err error) + Delete(userId uint, id uint) (err error) +} + +func NewResearchJournalsRepository(db *database.Database) ResearchJournalsRepository { + return &researchJournalsRepository{ + DB: db, + } +} + +func (_i *researchJournalsRepository) GetAll(userId uint, req request.ResearchJournalsQueryRequest) (researchJournals []*entity.ResearchJournals, paging paginator.Pagination, err error) { + query := _i.DB.DB.Where("user_id = ?", userId) + + // Apply filters + if req.JournalTitle != nil { + query = query.Where("journal_title ILIKE ?", "%"+*req.JournalTitle+"%") + } + if req.Publisher != nil { + query = query.Where("publisher ILIKE ?", "%"+*req.Publisher+"%") + } + if req.PublishedYear != nil { + query = query.Where("EXTRACT(YEAR FROM published_date) = ?", *req.PublishedYear) + } + + // Include user relationship + query = query.Preload("User") + + // Order by published date desc (most recent first), then by created_at desc + query = query.Order("published_date DESC NULLS LAST, created_at DESC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&researchJournals).Error + paging = *req.Pagination + + return +} + +func (_i *researchJournalsRepository) FindOneByUserAndId(userId uint, id uint) (researchJournal *entity.ResearchJournals, err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Preload("User").First(&researchJournal).Error + return +} + +func (_i *researchJournalsRepository) Create(researchJournal *entity.ResearchJournals) (result *entity.ResearchJournals, err error) { + err = _i.DB.DB.Create(researchJournal).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.DB.Preload("User").First(&result, researchJournal.ID).Error + return +} + +func (_i *researchJournalsRepository) Update(userId uint, id uint, researchJournal *entity.ResearchJournals) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Updates(researchJournal).Error + return +} + +func (_i *researchJournalsRepository) Delete(userId uint, id uint) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Delete(&entity.ResearchJournals{}).Error + return +} diff --git a/app/module/research_journals/request/research_journals.request.go b/app/module/research_journals/request/research_journals.request.go new file mode 100644 index 0000000..8260c2b --- /dev/null +++ b/app/module/research_journals/request/research_journals.request.go @@ -0,0 +1,72 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "time" +) + +type ResearchJournalsQueryRequest struct { + JournalTitle *string `json:"journalTitle"` + Publisher *string `json:"publisher"` + PublishedYear *int `json:"publishedYear"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type ResearchJournalsCreateRequest struct { + JournalTitle string `json:"journalTitle" validate:"required,min=2,max=500"` + Publisher string `json:"publisher" validate:"required,min=2,max=255"` + JournalURL string `json:"journalUrl" validate:"required,url"` + PublishedDate *time.Time `json:"publishedDate"` +} + +func (req ResearchJournalsCreateRequest) ToEntity() *entity.ResearchJournals { + return &entity.ResearchJournals{ + JournalTitle: req.JournalTitle, + Publisher: req.Publisher, + JournalURL: req.JournalURL, + PublishedDate: req.PublishedDate, + } +} + +type ResearchJournalsUpdateRequest struct { + JournalTitle string `json:"journalTitle" validate:"required,min=2,max=500"` + Publisher string `json:"publisher" validate:"required,min=2,max=255"` + JournalURL string `json:"journalUrl" validate:"required,url"` + PublishedDate *time.Time `json:"publishedDate"` +} + +func (req ResearchJournalsUpdateRequest) ToEntity() *entity.ResearchJournals { + return &entity.ResearchJournals{ + JournalTitle: req.JournalTitle, + Publisher: req.Publisher, + JournalURL: req.JournalURL, + PublishedDate: req.PublishedDate, + } +} + +type ResearchJournalsQueryRequestContext struct { + JournalTitle string `json:"journalTitle"` + Publisher string `json:"publisher"` + PublishedYear string `json:"publishedYear"` +} + +func (req ResearchJournalsQueryRequestContext) ToParamRequest() ResearchJournalsQueryRequest { + var request ResearchJournalsQueryRequest + + if journalTitle := req.JournalTitle; journalTitle != "" { + request.JournalTitle = &journalTitle + } + if publisher := req.Publisher; publisher != "" { + request.Publisher = &publisher + } + if publishedYearStr := req.PublishedYear; publishedYearStr != "" { + // Extract year from publishedYearStr if it's a date string + if publishedYearStr != "" { + // For now, we'll handle this in the repository layer + // This could be enhanced to parse date strings + } + } + + return request +} diff --git a/app/module/research_journals/research_journals.module.go b/app/module/research_journals/research_journals.module.go new file mode 100644 index 0000000..7ed17f5 --- /dev/null +++ b/app/module/research_journals/research_journals.module.go @@ -0,0 +1,54 @@ +package research_journals + +import ( + "narasi-ahli-be/app/module/research_journals/controller" + "narasi-ahli-be/app/module/research_journals/repository" + "narasi-ahli-be/app/module/research_journals/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of ResearchJournalsRouter +type ResearchJournalsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of ResearchJournals module +var NewResearchJournalsModule = fx.Options( + // register repository of ResearchJournals module + fx.Provide(repository.NewResearchJournalsRepository), + + // register service of ResearchJournals module + fx.Provide(service.NewResearchJournalsService), + + // register controller of ResearchJournals module + fx.Provide(controller.NewController), + + // register router of ResearchJournals module + fx.Provide(NewResearchJournalsRouter), +) + +// init ResearchJournalsRouter +func NewResearchJournalsRouter(fiber *fiber.App, controller *controller.Controller) *ResearchJournalsRouter { + return &ResearchJournalsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of ResearchJournals module +func (_i *ResearchJournalsRouter) RegisterResearchJournalsRoutes() { + // define controllers + researchJournalsController := _i.Controller.ResearchJournals + + // define routes + _i.App.Route("/research-journals", func(router fiber.Router) { + router.Get("/", researchJournalsController.All) + router.Get("/:id", researchJournalsController.Show) + router.Post("/", researchJournalsController.Save) + router.Put("/:id", researchJournalsController.Update) + router.Delete("/:id", researchJournalsController.Delete) + }) +} diff --git a/app/module/research_journals/response/research_journals.response.go b/app/module/research_journals/response/research_journals.response.go new file mode 100644 index 0000000..b5ae911 --- /dev/null +++ b/app/module/research_journals/response/research_journals.response.go @@ -0,0 +1,25 @@ +package response + +import ( + "time" +) + +type ResearchJournalsResponse struct { + ID uint `json:"id"` + UserID uint `json:"userId"` + JournalTitle string `json:"journalTitle"` + Publisher string `json:"publisher"` + JournalURL string `json:"journalUrl"` + PublishedDate *time.Time `json:"publishedDate"` + PublishedYear *int `json:"publishedYear"` // extracted from PublishedDate + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + User *UserBasicInfo `json:"user,omitempty"` +} + +type UserBasicInfo struct { + ID uint `json:"id"` + Username string `json:"username"` + Fullname string `json:"fullname"` + Email string `json:"email"` +} diff --git a/app/module/research_journals/service/research_journals.service.go b/app/module/research_journals/service/research_journals.service.go new file mode 100644 index 0000000..b49ba3c --- /dev/null +++ b/app/module/research_journals/service/research_journals.service.go @@ -0,0 +1,128 @@ +package service + +import ( + "errors" + "narasi-ahli-be/app/module/research_journals/mapper" + "narasi-ahli-be/app/module/research_journals/repository" + "narasi-ahli-be/app/module/research_journals/request" + "narasi-ahli-be/app/module/research_journals/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + + "github.com/rs/zerolog" +) + +type researchJournalsService struct { + Repo repository.ResearchJournalsRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +type ResearchJournalsService interface { + All(authToken string, req request.ResearchJournalsQueryRequest) (researchJournals []*response.ResearchJournalsResponse, paging paginator.Pagination, err error) + Show(authToken string, id uint) (researchJournal *response.ResearchJournalsResponse, err error) + Save(authToken string, req request.ResearchJournalsCreateRequest) (researchJournal *response.ResearchJournalsResponse, err error) + Update(authToken string, id uint, req request.ResearchJournalsUpdateRequest) (err error) + Delete(authToken string, id uint) error +} + +func NewResearchJournalsService(repo repository.ResearchJournalsRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger) ResearchJournalsService { + return &researchJournalsService{ + Repo: repo, + UsersRepo: usersRepo, + Log: log, + } +} + +func (_i *researchJournalsService) All(authToken string, req request.ResearchJournalsQueryRequest) (researchJournals []*response.ResearchJournalsResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, paginator.Pagination{}, errors.New("unauthorized") + } + + results, paging, err := _i.Repo.GetAll(userInfo.ID, req) + if err != nil { + return + } + + for _, result := range results { + researchJournals = append(researchJournals, mapper.ResearchJournalsResponseMapper(result)) + } + + return +} + +func (_i *researchJournalsService) Show(authToken string, id uint) (researchJournal *response.ResearchJournalsResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("unauthorized") + } + + result, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return nil, err + } + + return mapper.ResearchJournalsResponseMapper(result), nil +} + +func (_i *researchJournalsService) Save(authToken string, req request.ResearchJournalsCreateRequest) (researchJournal *response.ResearchJournalsResponse, err error) { + _i.Log.Info().Interface("data", req).Msg("Creating research journal") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("unauthorized") + } + + entity := req.ToEntity() + entity.UserID = userInfo.ID + + result, err := _i.Repo.Create(entity) + if err != nil { + return nil, err + } + + return mapper.ResearchJournalsResponseMapper(result), nil +} + +func (_i *researchJournalsService) Update(authToken string, id uint, req request.ResearchJournalsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Updating research journal") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("research journal not found") + } + + entity := req.ToEntity() + return _i.Repo.Update(userInfo.ID, id, entity) +} + +func (_i *researchJournalsService) Delete(authToken string, id uint) error { + _i.Log.Info().Str("authToken", authToken).Uint("id", id).Msg("Deleting research journal") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("research journal not found") + } + + return _i.Repo.Delete(userInfo.ID, id) +} diff --git a/app/module/subscription/controller/controller.go b/app/module/subscription/controller/controller.go new file mode 100644 index 0000000..7596d43 --- /dev/null +++ b/app/module/subscription/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/subscription/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + Subscription SubscriptionController +} + +func NewController(SubscriptionService service.SubscriptionService, log zerolog.Logger) *Controller { + return &Controller{ + Subscription: NewSubscriptionController(SubscriptionService, log), + } +} diff --git a/app/module/subscription/controller/subscription.controller.go b/app/module/subscription/controller/subscription.controller.go new file mode 100644 index 0000000..f9c7e5f --- /dev/null +++ b/app/module/subscription/controller/subscription.controller.go @@ -0,0 +1,198 @@ +package controller + +import ( + "narasi-ahli-be/app/module/subscription/request" + "narasi-ahli-be/app/module/subscription/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" +) + +type subscriptionController struct { + subscriptionService service.SubscriptionService + Log zerolog.Logger +} + +type SubscriptionController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewSubscriptionController(subscriptionService service.SubscriptionService, log zerolog.Logger) SubscriptionController { + return &subscriptionController{ + subscriptionService: subscriptionService, + Log: log, + } +} + +// All get all Subscription +// @Summary Get all Subscription +// @Description API for getting all Subscription +// @Tags Subscription +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.SubscriptionQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /subscription [get] +func (_i *subscriptionController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.SubscriptionQueryRequestContext{ + Email: c.Query("email"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + subscriptionData, paging, err := _i.subscriptionService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Subscription list successfully retrieved"}, + Data: subscriptionData, + Meta: paging, + }) +} + +// Show get one Subscription +// @Summary Get one Subscription +// @Description API for getting one Subscription +// @Tags Subscription +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Subscription ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /subscription/{id} [get] +func (_i *subscriptionController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + subscriptionData, err := _i.subscriptionService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Subscription successfully retrieved"}, + Data: subscriptionData, + }) +} + +// Save create Subscription +// @Summary Create Subscription +// @Description API for create Subscription +// @Tags Subscription +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.SubscriptionCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /subscription [post] +func (_i *subscriptionController) Save(c *fiber.Ctx) error { + req := new(request.SubscriptionCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + dataResult, err := _i.subscriptionService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Subscription successfully created"}, + Data: dataResult, + }) +} + +// Update update Subscription +// @Summary update Subscription +// @Description API for update Subscription +// @Tags Subscription +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.SubscriptionUpdateRequest true "Required payload" +// @Param id path int true "Subscription ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /subscription/{id} [put] +func (_i *subscriptionController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.SubscriptionUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.subscriptionService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Subscription successfully updated"}, + }) +} + +// Delete delete Subscription +// @Summary delete Subscription +// @Description API for delete Subscription +// @Tags Subscription +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Subscription ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /subscription/{id} [delete] +func (_i *subscriptionController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.subscriptionService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Subscription successfully deleted"}, + }) +} diff --git a/app/module/subscription/mapper/subscription.mapper.go b/app/module/subscription/mapper/subscription.mapper.go new file mode 100644 index 0000000..ce688e5 --- /dev/null +++ b/app/module/subscription/mapper/subscription.mapper.go @@ -0,0 +1,19 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/subscription/response" +) + +func SubscriptionResponseMapper(subscriptionReq *entity.Subscription) (subscriptionRes *res.SubscriptionResponse) { + if subscriptionReq != nil { + subscriptionRes = &res.SubscriptionResponse{ + ID: subscriptionReq.ID, + Email: subscriptionReq.Email, + IsActive: subscriptionReq.IsActive, + CreatedAt: subscriptionReq.CreatedAt, + UpdatedAt: subscriptionReq.UpdatedAt, + } + } + return subscriptionRes +} diff --git a/app/module/subscription/repository/subscription.repository.go b/app/module/subscription/repository/subscription.repository.go new file mode 100644 index 0000000..0ce6d82 --- /dev/null +++ b/app/module/subscription/repository/subscription.repository.go @@ -0,0 +1,93 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/subscription/request" + "narasi-ahli-be/utils/paginator" + "strings" + + "github.com/rs/zerolog" +) + +type subscriptionRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// SubscriptionRepository define interface of ISubscriptionRepository +type SubscriptionRepository interface { + GetAll(req request.SubscriptionQueryRequest) (subscriptions []*entity.Subscription, paging paginator.Pagination, err error) + FindOne(id uint) (subscription *entity.Subscription, err error) + Create(subscription *entity.Subscription) (subscriptionReturn *entity.Subscription, err error) + Update(id uint, subscription *entity.Subscription) (err error) + Delete(id uint) (err error) +} + +func NewSubscriptionRepository(db *database.Database, logger zerolog.Logger) SubscriptionRepository { + return &subscriptionRepository{ + DB: db, + Log: logger, + } +} + +// implement interface of ISubscriptionRepository +func (_i *subscriptionRepository) GetAll(req request.SubscriptionQueryRequest) (subscriptions []*entity.Subscription, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.Subscription{}) + + query = query.Where("is_active = ?", true) + + if req.Email != nil && *req.Email != "" { + email := strings.ToLower(*req.Email) + query = query.Where("LOWER(email) LIKE ?", "%"+strings.ToLower(email)+"%") + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&subscriptions).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *subscriptionRepository) FindOne(id uint) (subscription *entity.Subscription, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.First(&subscription).Error; err != nil { + return nil, err + } + + return subscription, nil +} + +func (_i *subscriptionRepository) Create(subscription *entity.Subscription) (subscriptionReturn *entity.Subscription, err error) { + result := _i.DB.DB.Create(subscription) + return subscription, result.Error +} + +func (_i *subscriptionRepository) Update(id uint, subscription *entity.Subscription) (err error) { + query := _i.DB.DB.Model(&entity.Subscription{}).Where(&entity.Subscription{ID: id}) + return query.Updates(subscription).Error +} + +func (_i *subscriptionRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&entity.Subscription{}).Where("id = ?", id) + return query.Delete(&entity.Subscription{}).Error +} diff --git a/app/module/subscription/request/subscription.request.go b/app/module/subscription/request/subscription.request.go new file mode 100644 index 0000000..36317ec --- /dev/null +++ b/app/module/subscription/request/subscription.request.go @@ -0,0 +1,53 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "time" +) + +type SubscriptionGeneric interface { + ToEntity() +} + +type SubscriptionQueryRequest struct { + Email *string `json:"email"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type SubscriptionCreateRequest struct { + Email string `json:"email" validate:"required,email"` +} + +func (req SubscriptionCreateRequest) ToEntity() *entity.Subscription { + return &entity.Subscription{ + Email: req.Email, + } +} + +type SubscriptionUpdateRequest struct { + ID uint `json:"id" validate:"required"` + Email string `json:"email" validate:"required,email"` +} + +func (req SubscriptionUpdateRequest) ToEntity() *entity.Subscription { + return &entity.Subscription{ + ID: req.ID, + Email: req.Email, + UpdatedAt: time.Now(), + } +} + +type SubscriptionQueryRequestContext struct { + Email string `json:"email"` +} + +func (req SubscriptionQueryRequestContext) ToParamRequest() SubscriptionQueryRequest { + var request SubscriptionQueryRequest + + if email := req.Email; email != "" { + request.Email = &email + } + + return request +} diff --git a/app/module/subscription/response/subscription.response.go b/app/module/subscription/response/subscription.response.go new file mode 100644 index 0000000..b25d2d1 --- /dev/null +++ b/app/module/subscription/response/subscription.response.go @@ -0,0 +1,11 @@ +package response + +import "time" + +type SubscriptionResponse struct { + ID uint `json:"id"` + Email string `json:"email"` + IsActive bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/subscription/service/subscription.service.go b/app/module/subscription/service/subscription.service.go new file mode 100644 index 0000000..7f13ba5 --- /dev/null +++ b/app/module/subscription/service/subscription.service.go @@ -0,0 +1,87 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/subscription/mapper" + "narasi-ahli-be/app/module/subscription/repository" + "narasi-ahli-be/app/module/subscription/request" + "narasi-ahli-be/app/module/subscription/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// SubscriptionService +type subscriptionService struct { + Repo repository.SubscriptionRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +// SubscriptionService define interface of ISubscriptionService +type SubscriptionService interface { + All(req request.SubscriptionQueryRequest) (subscription []*response.SubscriptionResponse, paging paginator.Pagination, err error) + Show(id uint) (subscription *response.SubscriptionResponse, err error) + Save(req request.SubscriptionCreateRequest) (subscription *entity.Subscription, err error) + Update(id uint, req request.SubscriptionUpdateRequest) (err error) + Delete(id uint) error +} + +// NewSubscriptionService init SubscriptionService +func NewSubscriptionService(repo repository.SubscriptionRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) SubscriptionService { + + return &subscriptionService{ + Repo: repo, + Log: log, + UsersRepo: usersRepo, + } +} + +// All implement interface of SubscriptionService +func (_i *subscriptionService) All(req request.SubscriptionQueryRequest) (subscriptions []*response.SubscriptionResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + subscriptions = append(subscriptions, mapper.SubscriptionResponseMapper(result)) + } + + return +} + +func (_i *subscriptionService) Show(id uint) (subscription *response.SubscriptionResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.SubscriptionResponseMapper(result), nil +} + +func (_i *subscriptionService) Save(req request.SubscriptionCreateRequest) (subscription *entity.Subscription, err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + return _i.Repo.Create(newReq) +} + +func (_i *subscriptionService) Update(id uint, req request.SubscriptionUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + entity := req.ToEntity() + + return _i.Repo.Update(id, entity) +} + +func (_i *subscriptionService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + result.IsActive = false + return _i.Repo.Update(id, result) +} diff --git a/app/module/subscription/subscription.module.go b/app/module/subscription/subscription.module.go new file mode 100644 index 0000000..dc88062 --- /dev/null +++ b/app/module/subscription/subscription.module.go @@ -0,0 +1,54 @@ +package subscription + +import ( + "narasi-ahli-be/app/module/subscription/controller" + "narasi-ahli-be/app/module/subscription/repository" + "narasi-ahli-be/app/module/subscription/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of SubscriptionRouter +type SubscriptionRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Subscription module +var NewSubscriptionModule = fx.Options( + // register repository of Subscription module + fx.Provide(repository.NewSubscriptionRepository), + + // register service of Subscription module + fx.Provide(service.NewSubscriptionService), + + // register controller of Subscription module + fx.Provide(controller.NewController), + + // register router of Subscription module + fx.Provide(NewSubscriptionRouter), +) + +// init SubscriptionRouter +func NewSubscriptionRouter(fiber *fiber.App, controller *controller.Controller) *SubscriptionRouter { + return &SubscriptionRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Subscription module +func (_i *SubscriptionRouter) RegisterSubscriptionRoutes() { + // define controllers + subscriptionController := _i.Controller.Subscription + + // define routes + _i.App.Route("/subscription", func(router fiber.Router) { + router.Get("/", subscriptionController.All) + router.Get("/:id", subscriptionController.Show) + router.Post("/", subscriptionController.Save) + router.Put("/:id", subscriptionController.Update) + router.Delete("/:id", subscriptionController.Delete) + }) +} diff --git a/app/module/user_levels/controller/controller.go b/app/module/user_levels/controller/controller.go new file mode 100644 index 0000000..a4f38d9 --- /dev/null +++ b/app/module/user_levels/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/user_levels/service" + +type Controller struct { + UserLevels UserLevelsController +} + +func NewController(UserLevelsService service.UserLevelsService) *Controller { + return &Controller{ + UserLevels: NewUserLevelsController(UserLevelsService), + } +} diff --git a/app/module/user_levels/controller/user_levels.controller.go b/app/module/user_levels/controller/user_levels.controller.go new file mode 100644 index 0000000..a07e4c0 --- /dev/null +++ b/app/module/user_levels/controller/user_levels.controller.go @@ -0,0 +1,257 @@ +package controller + +import ( + "narasi-ahli-be/app/module/user_levels/request" + "narasi-ahli-be/app/module/user_levels/service" + "narasi-ahli-be/utils/paginator" + "strconv" + "strings" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type userLevelsController struct { + userLevelsService service.UserLevelsService +} + +type UserLevelsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + ShowByAlias(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + EnableApproval(c *fiber.Ctx) error +} + +func NewUserLevelsController(userLevelsService service.UserLevelsService) UserLevelsController { + return &userLevelsController{ + userLevelsService: userLevelsService, + } +} + +// All UserLevels +// @Summary Get all UserLevels +// @Description API for getting all UserLevels +// @Tags UserLevels +// @Security Bearer +// @Param req query request.UserLevelsQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-levels [get] +func (_i *userLevelsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.UserLevelsQueryRequestContext{ + Name: c.Query("name"), + LevelNumber: c.Query("levelNumber"), + ParentLevelId: c.Query("parentLevelId"), + ProvinceId: c.Query("provinceId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + userLevelsData, paging, err := _i.userLevelsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevels list successfully retrieved"}, + Data: userLevelsData, + Meta: paging, + }) +} + +// Show UserLevels +// @Summary Get one UserLevels +// @Description API for getting one UserLevels +// @Tags UserLevels +// @Security Bearer +// @Param id path int true "UserLevels ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-levels/{id} [get] +func (_i *userLevelsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + userLevelsData, err := _i.userLevelsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserLevels successfully retrieved"}, + Data: userLevelsData, + }) +} + +// ShowByAlias UserLevels +// @Summary Get one UserLevels +// @Description API for getting one UserLevels +// @Tags UserLevels +// @Security Bearer +// @Param alias path string true "UserLevels Alias" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-levels/alias/{alias} [get] +func (_i *userLevelsController) ShowByAlias(c *fiber.Ctx) error { + alias := c.Params("alias") + userLevelsData, err := _i.userLevelsService.ShowByAlias(alias) + if err != nil { + return err + } + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserLevels successfully retrieved"}, + Data: userLevelsData, + }) +} + +// Save UserLevels +// @Summary Create UserLevels +// @Description API for create UserLevels +// @Tags UserLevels +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserLevelsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-levels [post] +func (_i *userLevelsController) Save(c *fiber.Ctx) error { + req := new(request.UserLevelsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + dataResult, err := _i.userLevelsService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevels successfully created"}, + Data: dataResult, + }) +} + +// Update UserLevels +// @Summary update UserLevels +// @Description API for update UserLevels +// @Tags UserLevels +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserLevelsUpdateRequest true "Required payload" +// @Param id path int true "UserLevels ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-levels/{id} [put] +func (_i *userLevelsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UserLevelsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.userLevelsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevels successfully updated"}, + }) +} + +// Delete UserLevels +// @Summary delete UserLevels +// @Description API for delete UserLevels +// @Tags UserLevels +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "UserLevels ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /user-levels/{id} [delete] +func (_i *userLevelsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.userLevelsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevels successfully deleted"}, + }) +} + +// EnableApproval Articles +// @Summary EnableApproval Articles +// @Description API for Enable Approval of Article +// @Tags UserLevels +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.UserLevelsApprovalRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-levels/enable-approval [post] +func (_i *userLevelsController) EnableApproval(c *fiber.Ctx) error { + req := new(request.UserLevelsApprovalRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + ids := strings.Split(req.Ids, ",") + for _, id := range ids { + idUint, err := strconv.ParseUint(id, 10, 64) + if err != nil { + return err + } + err = _i.userLevelsService.EnableApproval(uint(idUint), req.IsApprovalActive) + if err != nil { + return err + } + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"isApprovalActive of UserLevels successfully saved"}, + }) +} diff --git a/app/module/user_levels/mapper/user_levels.mapper.go b/app/module/user_levels/mapper/user_levels.mapper.go new file mode 100644 index 0000000..30561f0 --- /dev/null +++ b/app/module/user_levels/mapper/user_levels.mapper.go @@ -0,0 +1,25 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity/user_levels" + res "narasi-ahli-be/app/module/user_levels/response" +) + +func UserLevelsResponseMapper(userLevelsReq *user_levels.UserLevels) (userLevelsRes *res.UserLevelsResponse) { + if userLevelsReq != nil { + userLevelsRes = &res.UserLevelsResponse{ + ID: userLevelsReq.ID, + Name: userLevelsReq.Name, + AliasName: userLevelsReq.AliasName, + LevelNumber: userLevelsReq.LevelNumber, + ParentLevelId: userLevelsReq.ParentLevelId, + ProvinceId: userLevelsReq.ProvinceId, + IsApprovalActive: userLevelsReq.IsApprovalActive, + Group: userLevelsReq.Group, + IsActive: userLevelsReq.IsActive, + CreatedAt: userLevelsReq.CreatedAt, + UpdatedAt: userLevelsReq.UpdatedAt, + } + } + return userLevelsRes +} diff --git a/app/module/user_levels/repository/user_levels.repository.go b/app/module/user_levels/repository/user_levels.repository.go new file mode 100644 index 0000000..7a3c255 --- /dev/null +++ b/app/module/user_levels/repository/user_levels.repository.go @@ -0,0 +1,109 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity/user_levels" + "narasi-ahli-be/app/module/user_levels/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" +) + +type userLevelsRepository struct { + DB *database.Database +} + +// UserLevelsRepository define interface of IUserLevelsRepository +type UserLevelsRepository interface { + GetAll(req request.UserLevelsQueryRequest) (userLevelss []*user_levels.UserLevels, paging paginator.Pagination, err error) + FindOne(id uint) (userLevels *user_levels.UserLevels, err error) + FindOneByAlias(alias string) (userLevels *user_levels.UserLevels, err error) + Create(userLevels *user_levels.UserLevels) (userLevelsReturn *user_levels.UserLevels, err error) + Update(id uint, userLevels *user_levels.UserLevels) (err error) + Delete(id uint) (err error) +} + +func NewUserLevelsRepository(db *database.Database) UserLevelsRepository { + return &userLevelsRepository{ + DB: db, + } +} + +// implement interface of IUserLevelsRepository +func (_i *userLevelsRepository) GetAll(req request.UserLevelsQueryRequest) (userLevelss []*user_levels.UserLevels, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&user_levels.UserLevels{}) + query = query.Where("is_active = ?", true) + + if req.Name != nil && *req.Name != "" { + name := strings.ToLower(*req.Name) + query = query.Where("LOWER(name) LIKE ?", "%"+strings.ToLower(name)+"%") + } + if req.LevelNumber != nil { + query = query.Where("level_number = ?", req.LevelNumber) + } + if req.ParentLevelId != nil { + query = query.Where("parent_level_id = ?", req.ParentLevelId) + } + if req.ProvinceId != nil { + query = query.Where("province_id = ?", req.ProvinceId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&userLevelss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *userLevelsRepository) FindOne(id uint) (userLevels *user_levels.UserLevels, err error) { + if err := _i.DB.DB.First(&userLevels, id).Error; err != nil { + return nil, err + } + + return userLevels, nil +} + +func (_i *userLevelsRepository) FindOneByAlias(alias string) (userLevels *user_levels.UserLevels, err error) { + if err := _i.DB.DB.Where("alias_name = ?", strings.ToLower(alias)).First(&userLevels).Error; err != nil { + return nil, err + } + + return userLevels, nil +} + +func (_i *userLevelsRepository) Create(userLevels *user_levels.UserLevels) (userLevelsReturn *user_levels.UserLevels, err error) { + result := _i.DB.DB.Create(userLevels) + return userLevels, result.Error +} + +func (_i *userLevelsRepository) Update(id uint, userLevels *user_levels.UserLevels) (err error) { + userLevelsMap, err := utilSvc.StructToMap(userLevels) + if err != nil { + return err + } + return _i.DB.DB.Model(&user_levels.UserLevels{}). + Where(&user_levels.UserLevels{ID: id}). + Updates(userLevelsMap).Error +} + +func (_i *userLevelsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&user_levels.UserLevels{}, id).Error +} diff --git a/app/module/user_levels/repository/user_levels.repository.go~ b/app/module/user_levels/repository/user_levels.repository.go~ new file mode 100644 index 0000000..850d56b --- /dev/null +++ b/app/module/user_levels/repository/user_levels.repository.go~ @@ -0,0 +1,109 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/user_levels/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" +) + +type userLevelsRepository struct { + DB *database.Database +} + +// UserLevelsRepository define interface of IUserLevelsRepository +type UserLevelsRepository interface { + GetAll(req request.UserLevelsQueryRequest) (userLevelss []*entity.UserLevels, paging paginator.Pagination, err error) + FindOne(id uint) (userLevels *entity.UserLevels, err error) + FindOneByAlias(alias string) (userLevels *entity.UserLevels, err error) + Create(userLevels *entity.UserLevels) (userLevelsReturn *entity.UserLevels, err error) + Update(id uint, userLevels *entity.UserLevels) (err error) + Delete(id uint) (err error) +} + +func NewUserLevelsRepository(db *database.Database) UserLevelsRepository { + return &userLevelsRepository{ + DB: db, + } +} + +// implement interface of IUserLevelsRepository +func (_i *userLevelsRepository) GetAll(req request.UserLevelsQueryRequest) (userLevelss []*entity.UserLevels, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.UserLevels{}) + query = query.Where("is_active = ?", true) + + if req.Name != nil && *req.Name != "" { + name := strings.ToLower(*req.Name) + query = query.Where("LOWER(name) LIKE ?", "%"+strings.ToLower(name)+"%") + } + if req.LevelNumber != nil { + query = query.Where("level_number = ?", req.LevelNumber) + } + if req.ParentLevelId != nil { + query = query.Where("parent_level_id = ?", req.ParentLevelId) + } + if req.ProvinceId != nil { + query = query.Where("province_id = ?", req.ProvinceId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&userLevelss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *userLevelsRepository) FindOne(id uint) (userLevels *entity.UserLevels, err error) { + if err := _i.DB.DB.First(&userLevels, id).Error; err != nil { + return nil, err + } + + return userLevels, nil +} + +func (_i *userLevelsRepository) FindOneByAlias(alias string) (userLevels *entity.UserLevels, err error) { + if err := _i.DB.DB.Where("alias_name = ?", strings.ToLower(alias)).First(&userLevels).Error; err != nil { + return nil, err + } + + return userLevels, nil +} + +func (_i *userLevelsRepository) Create(userLevels *entity.UserLevels) (userLevelsReturn *entity.UserLevels, err error) { + result := _i.DB.DB.Create(userLevels) + return userLevels, result.Error +} + +func (_i *userLevelsRepository) Update(id uint, userLevels *entity.UserLevels) (err error) { + userLevelsMap, err := utilSvc.StructToMap(userLevels) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.UserLevels{}). + Where(&entity.UserLevels{ID: id}). + Updates(userLevelsMap).Error +} + +func (_i *userLevelsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.UserLevels{}, id).Error +} diff --git a/app/module/user_levels/request/user_levels.request.go b/app/module/user_levels/request/user_levels.request.go new file mode 100644 index 0000000..a641ee4 --- /dev/null +++ b/app/module/user_levels/request/user_levels.request.go @@ -0,0 +1,107 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity/user_levels" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type UserLevelsGeneric interface { + ToEntity() +} + +type UserLevelsQueryRequest struct { + Name *string `json:"name"` + LevelNumber *int `json:"levelNumber"` + ParentLevelId *int `json:"parentLevelId"` + ProvinceId *int `json:"provinceId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type UserLevelsCreateRequest struct { + Name string `json:"name" validate:"required"` + AliasName string `json:"aliasName" validate:"required"` + LevelNumber int `json:"levelNumber" validate:"required"` + ParentLevelId *int `json:"parentLevelId"` + ProvinceId *int `json:"provinceId"` + Group *string `json:"group"` + IsApprovalActive *bool `json:"isApprovalActive"` + IsActive *bool `json:"isActive"` +} + +func (req UserLevelsCreateRequest) ToEntity() *user_levels.UserLevels { + return &user_levels.UserLevels{ + Name: req.Name, + AliasName: req.AliasName, + LevelNumber: req.LevelNumber, + ParentLevelId: req.ParentLevelId, + ProvinceId: req.ProvinceId, + IsApprovalActive: req.IsApprovalActive, + Group: req.Group, + IsActive: req.IsActive, + } +} + +type UserLevelsUpdateRequest struct { + Name string `json:"name" validate:"required"` + AliasName string `json:"aliasName" validate:"required"` + LevelNumber int `json:"levelNumber" validate:"required"` + ParentLevelId *int `json:"parentLevelId"` + IsApprovalActive *bool `json:"isApprovalActive"` + Group *string `json:"group"` + ProvinceId *int `json:"provinceId"` +} + +func (req UserLevelsUpdateRequest) ToEntity() *user_levels.UserLevels { + return &user_levels.UserLevels{ + Name: req.Name, + AliasName: req.AliasName, + LevelNumber: req.LevelNumber, + ParentLevelId: req.ParentLevelId, + ProvinceId: req.ProvinceId, + IsApprovalActive: req.IsApprovalActive, + Group: req.Group, + UpdatedAt: time.Now(), + } +} + +type UserLevelsApprovalRequest struct { + Ids string `json:"ids" validate:"required"` + IsApprovalActive bool `json:"isApprovalActive" validate:"required"` +} + +type UserLevelsQueryRequestContext struct { + Name string `json:"name"` + LevelNumber string `json:"levelNumber"` + ParentLevelId string `json:"parentLevelId"` + ProvinceId string `json:"provinceId"` +} + +func (req UserLevelsQueryRequestContext) ToParamRequest() UserLevelsQueryRequest { + var request UserLevelsQueryRequest + + if name := req.Name; name != "" { + request.Name = &name + } + if levelNumberStr := req.LevelNumber; levelNumberStr != "" { + LevelNumber, err := strconv.Atoi(levelNumberStr) + if err == nil { + request.LevelNumber = &LevelNumber + } + } + if parentLevelIdStr := req.ParentLevelId; parentLevelIdStr != "" { + parentLevelId, err := strconv.Atoi(parentLevelIdStr) + if err == nil { + request.ParentLevelId = &parentLevelId + } + } + if provinceIdStr := req.ProvinceId; provinceIdStr != "" { + provinceId, err := strconv.Atoi(provinceIdStr) + if err == nil { + request.ProvinceId = &provinceId + } + } + + return request +} diff --git a/app/module/user_levels/response/user_levels.response.go b/app/module/user_levels/response/user_levels.response.go new file mode 100644 index 0000000..cdc2956 --- /dev/null +++ b/app/module/user_levels/response/user_levels.response.go @@ -0,0 +1,17 @@ +package response + +import "time" + +type UserLevelsResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + AliasName string `json:"aliasName"` + LevelNumber int `json:"levelNumber"` + ParentLevelId *int `json:"parentLevelId"` + ProvinceId *int `json:"provinceId"` + IsApprovalActive *bool `json:"isApprovalActive"` + Group *string `json:"group"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/app/module/user_levels/service/user_levels.service.go b/app/module/user_levels/service/user_levels.service.go new file mode 100644 index 0000000..56329e1 --- /dev/null +++ b/app/module/user_levels/service/user_levels.service.go @@ -0,0 +1,111 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity/user_levels" + "narasi-ahli-be/app/module/user_levels/mapper" + "narasi-ahli-be/app/module/user_levels/repository" + "narasi-ahli-be/app/module/user_levels/request" + "narasi-ahli-be/app/module/user_levels/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// UserLevelsService +type userLevelsService struct { + Repo repository.UserLevelsRepository + Log zerolog.Logger +} + +// UserLevelsService define interface of IUserLevelsService +type UserLevelsService interface { + All(req request.UserLevelsQueryRequest) (userLevels []*response.UserLevelsResponse, paging paginator.Pagination, err error) + Show(id uint) (userLevels *response.UserLevelsResponse, err error) + ShowByAlias(alias string) (userLevels *response.UserLevelsResponse, err error) + Save(req request.UserLevelsCreateRequest) (userLevels *user_levels.UserLevels, err error) + Update(id uint, req request.UserLevelsUpdateRequest) (err error) + Delete(id uint) error + EnableApproval(id uint, isApprovalActive bool) (err error) +} + +// NewUserLevelsService init UserLevelsService +func NewUserLevelsService(repo repository.UserLevelsRepository, log zerolog.Logger) UserLevelsService { + + return &userLevelsService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of UserLevelsService +func (_i *userLevelsService) All(req request.UserLevelsQueryRequest) (userLevelss []*response.UserLevelsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + userLevelss = append(userLevelss, mapper.UserLevelsResponseMapper(result)) + } + + return +} + +func (_i *userLevelsService) Show(id uint) (userLevels *response.UserLevelsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UserLevelsResponseMapper(result), nil +} + +func (_i *userLevelsService) ShowByAlias(alias string) (userLevels *response.UserLevelsResponse, err error) { + result, err := _i.Repo.FindOneByAlias(alias) + if err != nil { + return nil, err + } + + return mapper.UserLevelsResponseMapper(result), nil +} + +func (_i *userLevelsService) Save(req request.UserLevelsCreateRequest) (userLevels *user_levels.UserLevels, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + saveUserLevelsRes, err := _i.Repo.Create(req.ToEntity()) + if err != nil { + return nil, err + } + + return saveUserLevelsRes, nil +} + +func (_i *userLevelsService) Update(id uint, req request.UserLevelsUpdateRequest) (err error) { + //_i.Log.Info().Interface("data", req).Msg("") + + _i.Log.Info().Interface("data", req.ToEntity()).Msg("") + + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *userLevelsService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} + +func (_i *userLevelsService) EnableApproval(id uint, isApprovalActive bool) (err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + *result.IsApprovalActive = isApprovalActive + + return _i.Repo.Update(id, result) +} diff --git a/app/module/user_levels/user_levels.module.go b/app/module/user_levels/user_levels.module.go new file mode 100644 index 0000000..df014cb --- /dev/null +++ b/app/module/user_levels/user_levels.module.go @@ -0,0 +1,56 @@ +package user_levels + +import ( + "narasi-ahli-be/app/module/user_levels/controller" + "narasi-ahli-be/app/module/user_levels/repository" + "narasi-ahli-be/app/module/user_levels/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of UserLevelsRouter +type UserLevelsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of UserLevels module +var NewUserLevelsModule = fx.Options( + // register repository of UserLevels module + fx.Provide(repository.NewUserLevelsRepository), + + // register service of UserLevels module + fx.Provide(service.NewUserLevelsService), + + // register controller of UserLevels module + fx.Provide(controller.NewController), + + // register router of UserLevels module + fx.Provide(NewUserLevelsRouter), +) + +// init UserLevelsRouter +func NewUserLevelsRouter(fiber *fiber.App, controller *controller.Controller) *UserLevelsRouter { + return &UserLevelsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of UserLevels module +func (_i *UserLevelsRouter) RegisterUserLevelsRoutes() { + // define controllers + userLevelsController := _i.Controller.UserLevels + + // define routes + _i.App.Route("/user-levels", func(router fiber.Router) { + router.Get("/", userLevelsController.All) + router.Get("/:id", userLevelsController.Show) + router.Get("/alias/:alias", userLevelsController.ShowByAlias) + router.Post("/", userLevelsController.Save) + router.Put("/:id", userLevelsController.Update) + router.Delete("/:id", userLevelsController.Delete) + router.Put("/approval/:id", userLevelsController.EnableApproval) + }) +} diff --git a/app/module/user_role_accesses/controller/controller.go b/app/module/user_role_accesses/controller/controller.go new file mode 100644 index 0000000..57ddb33 --- /dev/null +++ b/app/module/user_role_accesses/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/user_role_accesses/service" + +type Controller struct { + UserRoleAccesses UserRoleAccessesController +} + +func NewController(UserRoleAccessesService service.UserRoleAccessesService) *Controller { + return &Controller{ + UserRoleAccesses: NewUserRoleAccessesController(UserRoleAccessesService), + } +} diff --git a/app/module/user_role_accesses/controller/user_role_accesses.controller.go b/app/module/user_role_accesses/controller/user_role_accesses.controller.go new file mode 100644 index 0000000..12b98f1 --- /dev/null +++ b/app/module/user_role_accesses/controller/user_role_accesses.controller.go @@ -0,0 +1,191 @@ +package controller + +import ( + "narasi-ahli-be/app/module/user_role_accesses/request" + "narasi-ahli-be/app/module/user_role_accesses/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type userRoleAccessesController struct { + userRoleAccessesService service.UserRoleAccessesService +} + +type UserRoleAccessesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewUserRoleAccessesController(userRoleAccessesService service.UserRoleAccessesService) UserRoleAccessesController { + return &userRoleAccessesController{ + userRoleAccessesService: userRoleAccessesService, + } +} + +// All UserRoleAccesses +// @Summary Get all UserRoleAccesses +// @Description API for getting all UserRoleAccesses +// @Tags UserRoleAccesses +// @Security Bearer +// @Param req query request.UserRoleAccessesQueryRequest false "query parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-role-accesses [get] +func (_i *userRoleAccessesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.UserRoleAccessesQueryRequestContext{ + MenuId: c.Query("menuId"), + UserRoleId: c.Query("userRoleId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + userRoleAccessesData, paging, err := _i.userRoleAccessesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoleAccesses list successfully retrieved"}, + Data: userRoleAccessesData, + Meta: paging, + }) +} + +// Show UserRoleAccesses +// @Summary Get one UserRoleAccesses +// @Description API for getting one UserRoleAccesses +// @Tags UserRoleAccesses +// @Security Bearer +// @Param id path int true "UserRoleAccesses ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-role-accesses/{id} [get] +func (_i *userRoleAccessesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + userRoleAccessesData, err := _i.userRoleAccessesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoleAccesses successfully retrieved"}, + Data: userRoleAccessesData, + }) +} + +// Save UserRoleAccesses +// @Summary Create UserRoleAccesses +// @Description API for create UserRoleAccesses +// @Tags UserRoleAccesses +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserRoleAccessesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-role-accesses [post] +func (_i *userRoleAccessesController) Save(c *fiber.Ctx) error { + req := new(request.UserRoleAccessesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.userRoleAccessesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoleAccesses successfully created"}, + }) +} + +// Update UserRoleAccesses +// @Summary update UserRoleAccesses +// @Description API for update UserRoleAccesses +// @Tags UserRoleAccesses +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserRoleAccessesUpdateRequest true "Required payload" +// @Param id path int true "UserRoleAccesses ID" +// @Success 200 {object} response.Response +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-role-accesses/{id} [put] +func (_i *userRoleAccessesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UserRoleAccessesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.userRoleAccessesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoleAccesses successfully updated"}, + }) +} + +// Delete UserRoleAccesses +// @Summary delete UserRoleAccesses +// @Description API for delete UserRoleAccesses +// @Tags UserRoleAccesses +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "UserRoleAccesses ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-role-accesses/{id} [delete] +func (_i *userRoleAccessesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.userRoleAccessesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoleAccesses successfully deleted"}, + }) +} diff --git a/app/module/user_role_accesses/entity/user_role_accesses.entity.go b/app/module/user_role_accesses/entity/user_role_accesses.entity.go new file mode 100644 index 0000000..3d1c58d --- /dev/null +++ b/app/module/user_role_accesses/entity/user_role_accesses.entity.go @@ -0,0 +1,18 @@ +package entity + +import "time" + +type UserRoleAccesses struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserRoleId int `json:"user_role_id" gorm:"type:int4"` + MenuId int `json:"menu_id" gorm:"type:int4"` + IsViewEnabled bool `json:"is_view_enabled" gorm:"type:bool"` + IsInsertEnabled bool `json:"is_insert_enabled" gorm:"type:bool"` + IsUpdateEnabled bool `json:"is_update_enabled" gorm:"type:bool"` + IsDeleteEnabled bool `json:"is_delete_enabled" gorm:"type:bool"` + IsApprovalEnabled bool `json:"is_approval_enabled" gorm:"type:bool"` + IsAdminEnabled bool `json:"is_admin_enabled" gorm:"type:bool"` + IsActive bool `json:"is_active" gorm:"type:bool"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` +} \ No newline at end of file diff --git a/app/module/user_role_accesses/mapper/user_role_accesses.mapper.go b/app/module/user_role_accesses/mapper/user_role_accesses.mapper.go new file mode 100644 index 0000000..0e62370 --- /dev/null +++ b/app/module/user_role_accesses/mapper/user_role_accesses.mapper.go @@ -0,0 +1,26 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/user_role_accesses/response" +) + +func UserRoleAccessesResponseMapper(userRoleAccessesReq *entity.UserRoleAccesses) (userRoleAccessesRes *res.UserRoleAccessesResponse) { + if userRoleAccessesReq != nil { + userRoleAccessesRes = &res.UserRoleAccessesResponse{ + ID: userRoleAccessesReq.ID, + UserRoleId: userRoleAccessesReq.UserRoleId, + MenuId: userRoleAccessesReq.MenuId, + IsViewEnabled: userRoleAccessesReq.IsViewEnabled, + IsInsertEnabled: userRoleAccessesReq.IsInsertEnabled, + IsUpdateEnabled: userRoleAccessesReq.IsUpdateEnabled, + IsDeleteEnabled: userRoleAccessesReq.IsDeleteEnabled, + IsApprovalEnabled: userRoleAccessesReq.IsApprovalEnabled, + IsAdminEnabled: userRoleAccessesReq.IsAdminEnabled, + IsActive: userRoleAccessesReq.IsActive, + CreatedAt: userRoleAccessesReq.CreatedAt, + UpdatedAt: userRoleAccessesReq.UpdatedAt, + } + } + return userRoleAccessesRes +} diff --git a/app/module/user_role_accesses/repository/user_role_accesses.repository.go b/app/module/user_role_accesses/repository/user_role_accesses.repository.go new file mode 100644 index 0000000..fa8460d --- /dev/null +++ b/app/module/user_role_accesses/repository/user_role_accesses.repository.go @@ -0,0 +1,82 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/user_role_accesses/request" + "narasi-ahli-be/utils/paginator" +) + +type userRoleAccessesRepository struct { + DB *database.Database +} + +// UserRoleAccessesRepository define interface of IUserRoleAccessesRepository +type UserRoleAccessesRepository interface { + GetAll(req request.UserRoleAccessesQueryRequest) (userRoleAccessess []*entity.UserRoleAccesses, paging paginator.Pagination, err error) + FindOne(id uint) (userRoleAccesses *entity.UserRoleAccesses, err error) + Create(userRoleAccesses *entity.UserRoleAccesses) (err error) + CreateAll(userRoleAccesses *[]entity.UserRoleAccesses) (err error) + Update(id uint, userRoleAccesses *entity.UserRoleAccesses) (err error) + Delete(id uint) (err error) +} + +func NewUserRoleAccessesRepository(db *database.Database) UserRoleAccessesRepository { + return &userRoleAccessesRepository{ + DB: db, + } +} + +// implement interface of IUserRoleAccessesRepository +func (_i *userRoleAccessesRepository) GetAll(req request.UserRoleAccessesQueryRequest) (userRoleAccessess []*entity.UserRoleAccesses, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.UserRoleAccesses{}) + query = query.Where("is_active = ?", true) + + if req.UserRoleId != nil { + query = query.Where("user_role_id = ?", req.UserRoleId) + } + if req.MenuId != nil { + query = query.Where("menu_id = ?", req.MenuId) + } + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&userRoleAccessess).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *userRoleAccessesRepository) FindOne(id uint) (userRoleAccesses *entity.UserRoleAccesses, err error) { + if err := _i.DB.DB.First(&userRoleAccesses, id).Error; err != nil { + return nil, err + } + + return userRoleAccesses, nil +} + +func (_i *userRoleAccessesRepository) Create(userRoleAccesses *entity.UserRoleAccesses) (err error) { + return _i.DB.DB.Create(userRoleAccesses).Error +} + +func (_i *userRoleAccessesRepository) CreateAll(userRoleAccesses *[]entity.UserRoleAccesses) (err error) { + return _i.DB.DB.Create(userRoleAccesses).Error +} + +func (_i *userRoleAccessesRepository) Update(id uint, userRoleAccesses *entity.UserRoleAccesses) (err error) { + return _i.DB.DB.Model(&entity.UserRoleAccesses{}). + Where(&entity.UserRoleAccesses{ID: id}). + Updates(userRoleAccesses).Error +} + +func (_i *userRoleAccessesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.UserRoleAccesses{}, id).Error +} diff --git a/app/module/user_role_accesses/request/user_role_accesses.request.go b/app/module/user_role_accesses/request/user_role_accesses.request.go new file mode 100644 index 0000000..2935d84 --- /dev/null +++ b/app/module/user_role_accesses/request/user_role_accesses.request.go @@ -0,0 +1,93 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type UserRoleAccessesGeneric interface { + ToEntity() +} + +type UserRoleAccessesQueryRequest struct { + UserRoleId *int `json:"userRoleId" validate:"required"` + MenuId *int `json:"menuId" validate:"required"` + IsActive *bool `json:"isActive" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type UserRoleAccessesCreateRequest struct { + MenuId int `json:"menuId" validate:"required"` + IsViewEnabled bool `json:"isViewEnabled" validate:"required"` + IsInsertEnabled bool `json:"isInsertEnabled" validate:"required"` + IsUpdateEnabled bool `json:"isUpdateEnabled" validate:"required"` + IsDeleteEnabled bool `json:"isDeleteEnabled" validate:"required"` + IsApprovalEnabled bool `json:"isApprovalEnabled" validate:"required"` + IsAdminEnabled bool `json:"isAdminEnabled" validate:"required"` +} + +func (req UserRoleAccessesCreateRequest) ToEntity() *entity.UserRoleAccesses { + return &entity.UserRoleAccesses{ + MenuId: req.MenuId, + IsViewEnabled: req.IsViewEnabled, + IsInsertEnabled: req.IsInsertEnabled, + IsUpdateEnabled: req.IsUpdateEnabled, + IsDeleteEnabled: req.IsDeleteEnabled, + IsApprovalEnabled: req.IsApprovalEnabled, + IsAdminEnabled: req.IsAdminEnabled, + } +} + +type UserRoleAccessesUpdateRequest struct { + ID uint `json:"id" validate:"required"` + UserRoleId uint `json:"user_role_id" validate:"required"` + MenuId int `json:"menu_id" validate:"required"` + IsViewEnabled bool `json:"is_view_enabled" validate:"required"` + IsInsertEnabled bool `json:"is_insert_enabled" validate:"required"` + IsUpdateEnabled bool `json:"is_update_enabled" validate:"required"` + IsDeleteEnabled bool `json:"is_delete_enabled" validate:"required"` + IsApprovalEnabled bool `json:"is_approval_enabled" validate:"required"` + IsAdminEnabled bool `json:"is_admin_enabled" validate:"required"` +} + +func (req UserRoleAccessesUpdateRequest) ToEntity() *entity.UserRoleAccesses { + return &entity.UserRoleAccesses{ + ID: req.ID, + UserRoleId: req.UserRoleId, + MenuId: req.MenuId, + IsViewEnabled: req.IsViewEnabled, + IsInsertEnabled: req.IsInsertEnabled, + IsUpdateEnabled: req.IsUpdateEnabled, + IsDeleteEnabled: req.IsDeleteEnabled, + IsApprovalEnabled: req.IsApprovalEnabled, + IsAdminEnabled: req.IsAdminEnabled, + UpdatedAt: time.Now(), + } +} + +type UserRoleAccessesQueryRequestContext struct { + UserRoleId string `json:"userRoleId"` + MenuId string `json:"menuId"` +} + +func (req UserRoleAccessesQueryRequestContext) ToParamRequest() UserRoleAccessesQueryRequest { + var request UserRoleAccessesQueryRequest + + if userRoleIdStr := req.UserRoleId; userRoleIdStr != "" { + userRoleId, err := strconv.Atoi(userRoleIdStr) + if err == nil { + request.UserRoleId = &userRoleId + } + } + + if menuIdStr := req.MenuId; menuIdStr != "" { + menuId, err := strconv.Atoi(menuIdStr) + if err == nil { + request.MenuId = &menuId + } + } + + return request +} diff --git a/app/module/user_role_accesses/response/user_role_accesses.response.go b/app/module/user_role_accesses/response/user_role_accesses.response.go new file mode 100644 index 0000000..c9d0f70 --- /dev/null +++ b/app/module/user_role_accesses/response/user_role_accesses.response.go @@ -0,0 +1,18 @@ +package response + +import "time" + +type UserRoleAccessesResponse struct { + ID uint `json:"id"` + UserRoleId uint `json:"user_role_id"` + MenuId int `json:"menu_id"` + IsViewEnabled bool `json:"is_view_enabled"` + IsInsertEnabled bool `json:"is_insert_enabled"` + IsUpdateEnabled bool `json:"is_update_enabled"` + IsDeleteEnabled bool `json:"is_delete_enabled"` + IsApprovalEnabled bool `json:"is_approval_enabled"` + IsAdminEnabled bool `json:"is_admin_enabled"` + IsActive *bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/user_role_accesses/service/user_role_accesses.service.go b/app/module/user_role_accesses/service/user_role_accesses.service.go new file mode 100644 index 0000000..e34fb73 --- /dev/null +++ b/app/module/user_role_accesses/service/user_role_accesses.service.go @@ -0,0 +1,80 @@ +package service + +import ( + "narasi-ahli-be/app/module/user_role_accesses/mapper" + "narasi-ahli-be/app/module/user_role_accesses/repository" + "narasi-ahli-be/app/module/user_role_accesses/request" + "narasi-ahli-be/app/module/user_role_accesses/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// UserRoleAccessesService +type userRoleAccessesService struct { + Repo repository.UserRoleAccessesRepository + Log zerolog.Logger +} + +// UserRoleAccessesService define interface of IUserRoleAccessesService +type UserRoleAccessesService interface { + All(req request.UserRoleAccessesQueryRequest) (userRoleAccesses []*response.UserRoleAccessesResponse, paging paginator.Pagination, err error) + Show(id uint) (userRoleAccesses *response.UserRoleAccessesResponse, err error) + Save(req request.UserRoleAccessesCreateRequest) (err error) + Update(id uint, req request.UserRoleAccessesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewUserRoleAccessesService init UserRoleAccessesService +func NewUserRoleAccessesService(repo repository.UserRoleAccessesRepository, log zerolog.Logger) UserRoleAccessesService { + + return &userRoleAccessesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of UserRoleAccessesService +func (_i *userRoleAccessesService) All(req request.UserRoleAccessesQueryRequest) (userRoleAccessess []*response.UserRoleAccessesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + userRoleAccessess = append(userRoleAccessess, mapper.UserRoleAccessesResponseMapper(result)) + } + + return +} + +func (_i *userRoleAccessesService) Show(id uint) (userRoleAccesses *response.UserRoleAccessesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UserRoleAccessesResponseMapper(result), nil +} + +func (_i *userRoleAccessesService) Save(req request.UserRoleAccessesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *userRoleAccessesService) Update(id uint, req request.UserRoleAccessesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *userRoleAccessesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} diff --git a/app/module/user_role_accesses/user_role_accesses.module.go b/app/module/user_role_accesses/user_role_accesses.module.go new file mode 100644 index 0000000..92008db --- /dev/null +++ b/app/module/user_role_accesses/user_role_accesses.module.go @@ -0,0 +1,54 @@ +package user_role_accesses + +import ( + "narasi-ahli-be/app/module/user_role_accesses/controller" + "narasi-ahli-be/app/module/user_role_accesses/repository" + "narasi-ahli-be/app/module/user_role_accesses/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of UserRoleAccessesRouter +type UserRoleAccessesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of UserRoleAccesses module +var NewUserRoleAccessesModule = fx.Options( + // register repository of UserRoleAccesses module + fx.Provide(repository.NewUserRoleAccessesRepository), + + // register service of UserRoleAccesses module + fx.Provide(service.NewUserRoleAccessesService), + + // register controller of UserRoleAccesses module + fx.Provide(controller.NewController), + + // register router of UserRoleAccesses module + fx.Provide(NewUserRoleAccessesRouter), +) + +// init UserRoleAccessesRouter +func NewUserRoleAccessesRouter(fiber *fiber.App, controller *controller.Controller) *UserRoleAccessesRouter { + return &UserRoleAccessesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of UserRoleAccesses module +func (_i *UserRoleAccessesRouter) RegisterUserRoleAccessesRoutes() { + // define controllers + userRoleAccessesController := _i.Controller.UserRoleAccesses + + // define routes + _i.App.Route("/user-role-accesses", func(router fiber.Router) { + router.Get("/", userRoleAccessesController.All) + router.Get("/:id", userRoleAccessesController.Show) + router.Post("/", userRoleAccessesController.Save) + router.Put("/:id", userRoleAccessesController.Update) + router.Delete("/:id", userRoleAccessesController.Delete) + }) +} diff --git a/app/module/user_role_level_details/controller/controller.go b/app/module/user_role_level_details/controller/controller.go new file mode 100644 index 0000000..da35565 --- /dev/null +++ b/app/module/user_role_level_details/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/user_role_level_details/service" + +type Controller struct { + UserRoleLevelDetails UserRoleLevelDetailsController +} + +func NewController(UserRoleLevelDetailsService service.UserRoleLevelDetailsService) *Controller { + return &Controller{ + UserRoleLevelDetails: NewUserRoleLevelDetailsController(UserRoleLevelDetailsService), + } +} diff --git a/app/module/user_role_level_details/controller/user_role_level_details.controller.go b/app/module/user_role_level_details/controller/user_role_level_details.controller.go new file mode 100644 index 0000000..76c910c --- /dev/null +++ b/app/module/user_role_level_details/controller/user_role_level_details.controller.go @@ -0,0 +1,182 @@ +package controller + +import ( + "narasi-ahli-be/app/module/user_role_level_details/request" + "narasi-ahli-be/app/module/user_role_level_details/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type userRoleLevelDetailsController struct { + userRoleLevelDetailsService service.UserRoleLevelDetailsService +} + +type UserRoleLevelDetailsController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewUserRoleLevelDetailsController(userRoleLevelDetailsService service.UserRoleLevelDetailsService) UserRoleLevelDetailsController { + return &userRoleLevelDetailsController{ + userRoleLevelDetailsService: userRoleLevelDetailsService, + } +} + +// All get all UserRoleLevelDetails +// @Summary Get all UserRoleLevelDetails +// @Description API for getting all UserRoleLevelDetails +// @Tags Task +// @Security Bearer +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /user-role-level-details [get] +func (_i *userRoleLevelDetailsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + var req request.UserRoleLevelDetailsQueryRequest + req.Pagination = paginate + + userRoleLevelDetailsData, paging, err := _i.userRoleLevelDetailsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserRoleLevelDetails list successfully retrieved"}, + Data: userRoleLevelDetailsData, + Meta: paging, + }) +} + +// Show get one UserRoleLevelDetails +// @Summary Get one UserRoleLevelDetails +// @Description API for getting one UserRoleLevelDetails +// @Tags Task +// @Security Bearer +// @Param id path int true "UserRoleLevelDetails ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /user-role-level-details/{id} [get] +func (_i *userRoleLevelDetailsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + userRoleLevelDetailsData, err := _i.userRoleLevelDetailsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserRoleLevelDetails successfully retrieved"}, + Data: userRoleLevelDetailsData, + }) +} + +// Save create UserRoleLevelDetails +// @Summary Create UserRoleLevelDetails +// @Description API for create UserRoleLevelDetails +// @Tags Task +// @Security Bearer +// @Body request.UserRoleLevelDetailsCreateRequest +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /user-role-level-details [post] +func (_i *userRoleLevelDetailsController) Save(c *fiber.Ctx) error { + req := new(request.UserRoleLevelDetailsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.userRoleLevelDetailsService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserRoleLevelDetails successfully created"}, + }) +} + +// Update update UserRoleLevelDetails +// @Summary update UserRoleLevelDetails +// @Description API for update UserRoleLevelDetails +// @Tags Task +// @Security Bearer +// @Body request.UserRoleLevelDetailsUpdateRequest +// @Param id path int true "UserRoleLevelDetails ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /user-role-level-details/{id} [put] +func (_i *userRoleLevelDetailsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UserRoleLevelDetailsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.userRoleLevelDetailsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserRoleLevelDetails successfully updated"}, + }) +} + +// Delete delete UserRoleLevelDetails +// @Summary delete UserRoleLevelDetails +// @Description API for delete UserRoleLevelDetails +// @Tags Task +// @Security Bearer +// @Param id path int true "UserRoleLevelDetails ID" +// @Success 200 {object} response.Response +// @Failure 401 {object} response.Response +// @Failure 404 {object} response.Response +// @Failure 422 {object} response.Response +// @Failure 500 {object} response.Response +// @Router /user-role-level-details/{id} [delete] +func (_i *userRoleLevelDetailsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.userRoleLevelDetailsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Messages: utilRes.Messages{"UserRoleLevelDetails successfully deleted"}, + }) +} diff --git a/app/module/user_role_level_details/mapper/user_role_level_details.mapper.go b/app/module/user_role_level_details/mapper/user_role_level_details.mapper.go new file mode 100644 index 0000000..1722f6c --- /dev/null +++ b/app/module/user_role_level_details/mapper/user_role_level_details.mapper.go @@ -0,0 +1,20 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/user_role_level_details/response" +) + +func UserRoleLevelDetailsResponseMapper(userRoleLevelDetailsReq *entity.UserRoleLevelDetails) (userRoleLevelDetailsRes *res.UserRoleLevelDetailsResponse) { + if userRoleLevelDetailsReq != nil { + userRoleLevelDetailsRes = &res.UserRoleLevelDetailsResponse{ + ID: userRoleLevelDetailsReq.ID, + UserRoleId: userRoleLevelDetailsReq.UserRoleId, + UserLevelId: userRoleLevelDetailsReq.UserLevelId, + IsActive: userRoleLevelDetailsReq.IsActive, + CreatedAt: userRoleLevelDetailsReq.CreatedAt, + UpdatedAt: userRoleLevelDetailsReq.UpdatedAt, + } + } + return userRoleLevelDetailsRes +} diff --git a/app/module/user_role_level_details/repository/user_role_level_details.repository.go b/app/module/user_role_level_details/repository/user_role_level_details.repository.go new file mode 100644 index 0000000..1823fd8 --- /dev/null +++ b/app/module/user_role_level_details/repository/user_role_level_details.repository.go @@ -0,0 +1,83 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/user_role_level_details/request" + "narasi-ahli-be/utils/paginator" +) + +type userRoleLevelDetailsRepository struct { + DB *database.Database +} + +// UserRoleLevelDetailsRepository define interface of IUserRoleLevelDetailsRepository +type UserRoleLevelDetailsRepository interface { + GetAll(req request.UserRoleLevelDetailsQueryRequest) (userRoleLevelDetailss []*entity.UserRoleLevelDetails, paging paginator.Pagination, err error) + FindOne(id uint) (userRoleLevelDetails *entity.UserRoleLevelDetails, err error) + FindByUserLevels(userLevelId uint) (userRoleLevelDetails []*entity.UserRoleLevelDetails, err error) + Create(userRoleLevelDetails *entity.UserRoleLevelDetails) (err error) + CreateAll(userRoleLevelDetails *[]entity.UserRoleLevelDetails) (err error) + Update(id uint, userRoleLevelDetails *entity.UserRoleLevelDetails) (err error) + Delete(id uint) (err error) +} + +func NewUserRoleLevelDetailsRepository(db *database.Database) UserRoleLevelDetailsRepository { + return &userRoleLevelDetailsRepository{ + DB: db, + } +} + +// implement interface of IUserRoleLevelDetailsRepository +func (_i *userRoleLevelDetailsRepository) GetAll(req request.UserRoleLevelDetailsQueryRequest) (userRoleLevelDetailss []*entity.UserRoleLevelDetails, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.UserRoleLevelDetails{}) + query.Count(&count) + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&userRoleLevelDetailss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *userRoleLevelDetailsRepository) FindOne(id uint) (userRoleLevelDetails *entity.UserRoleLevelDetails, err error) { + if err := _i.DB.DB.First(&userRoleLevelDetails, id).Error; err != nil { + return nil, err + } + + return userRoleLevelDetails, nil +} + +func (_i *userRoleLevelDetailsRepository) FindByUserLevels(userLevelId uint) (userRoleLevelDetails []*entity.UserRoleLevelDetails, err error) { + if err := _i.DB.DB.Where(&entity.UserRoleLevelDetails{UserLevelId: userLevelId}).Find(&userRoleLevelDetails).Error; err != nil { + return nil, err + } + + return userRoleLevelDetails, nil +} + +func (_i *userRoleLevelDetailsRepository) Create(userRoleLevelDetails *entity.UserRoleLevelDetails) (err error) { + return _i.DB.DB.Create(userRoleLevelDetails).Error +} + +func (_i *userRoleLevelDetailsRepository) CreateAll(userRoleLevelDetails *[]entity.UserRoleLevelDetails) (err error) { + return _i.DB.DB.Create(userRoleLevelDetails).Error +} + +func (_i *userRoleLevelDetailsRepository) Update(id uint, userRoleLevelDetails *entity.UserRoleLevelDetails) (err error) { + return _i.DB.DB.Model(&entity.UserRoleLevelDetails{}). + Where(&entity.UserRoleLevelDetails{ID: id}). + Updates(userRoleLevelDetails).Error +} + +func (_i *userRoleLevelDetailsRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.UserRoleLevelDetails{}, id).Error +} diff --git a/app/module/user_role_level_details/request/user_role_level_details.request.go b/app/module/user_role_level_details/request/user_role_level_details.request.go new file mode 100644 index 0000000..580d238 --- /dev/null +++ b/app/module/user_role_level_details/request/user_role_level_details.request.go @@ -0,0 +1,51 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "time" +) + +type UserRoleLevelDetailsGeneric interface { + ToEntity() +} + +type UserRoleLevelDetailsQueryRequest struct { + UserRoleId uint `json:"user_role_id" validate:"required"` + UserLevelId uint `json:"user_level_id" validate:"required"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type UserRoleLevelDetailsCreateRequest struct { + UserRoleId uint `json:"user_role_id" validate:"required"` + UserLevelId uint `json:"user_level_id" validate:"required"` + IsActive *bool `json:"is_active" validate:"required"` +} + +func (req UserRoleLevelDetailsCreateRequest) ToEntity() *entity.UserRoleLevelDetails { + return &entity.UserRoleLevelDetails{ + UserRoleId: req.UserRoleId, + UserLevelId: req.UserLevelId, + IsActive: req.IsActive, + } +} + +type UserRoleLevelDetailsUpdateRequest struct { + ID uint `json:"id" validate:"required"` + UserRoleId uint `json:"user_role_id" validate:"required"` + UserLevelId uint `json:"user_level_id" validate:"required"` + IsActive *bool `json:"is_active" validate:"required"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +func (req UserRoleLevelDetailsUpdateRequest) ToEntity() *entity.UserRoleLevelDetails { + return &entity.UserRoleLevelDetails{ + ID: req.ID, + UserRoleId: req.UserRoleId, + UserLevelId: req.UserLevelId, + IsActive: req.IsActive, + CreatedAt: req.CreatedAt, + UpdatedAt: req.UpdatedAt, + } +} diff --git a/app/module/user_role_level_details/response/user_role_level_details.response.go b/app/module/user_role_level_details/response/user_role_level_details.response.go new file mode 100644 index 0000000..58ed186 --- /dev/null +++ b/app/module/user_role_level_details/response/user_role_level_details.response.go @@ -0,0 +1,12 @@ +package response + +import "time" + +type UserRoleLevelDetailsResponse struct { + ID uint `json:"id"` + UserRoleId uint `json:"user_role_id"` + UserLevelId uint `json:"user_level_id"` + IsActive *bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/user_role_level_details/service/user_role_level_details.service.go b/app/module/user_role_level_details/service/user_role_level_details.service.go new file mode 100644 index 0000000..318c397 --- /dev/null +++ b/app/module/user_role_level_details/service/user_role_level_details.service.go @@ -0,0 +1,73 @@ +package service + +import ( + "narasi-ahli-be/app/module/user_role_level_details/mapper" + "narasi-ahli-be/app/module/user_role_level_details/repository" + "narasi-ahli-be/app/module/user_role_level_details/request" + "narasi-ahli-be/app/module/user_role_level_details/response" + "narasi-ahli-be/utils/paginator" + + "github.com/rs/zerolog" +) + +// UserRoleLevelDetailsService +type userRoleLevelDetailsService struct { + Repo repository.UserRoleLevelDetailsRepository + Log zerolog.Logger +} + +// UserRoleLevelDetailsService define interface of IUserRoleLevelDetailsService +type UserRoleLevelDetailsService interface { + All(req request.UserRoleLevelDetailsQueryRequest) (userRoleLevelDetails []*response.UserRoleLevelDetailsResponse, paging paginator.Pagination, err error) + Show(id uint) (userRoleLevelDetails *response.UserRoleLevelDetailsResponse, err error) + Save(req request.UserRoleLevelDetailsCreateRequest) (err error) + Update(id uint, req request.UserRoleLevelDetailsUpdateRequest) (err error) + Delete(id uint) error +} + +// NewUserRoleLevelDetailsService init UserRoleLevelDetailsService +func NewUserRoleLevelDetailsService(repo repository.UserRoleLevelDetailsRepository, log zerolog.Logger) UserRoleLevelDetailsService { + + return &userRoleLevelDetailsService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of UserRoleLevelDetailsService +func (_i *userRoleLevelDetailsService) All(req request.UserRoleLevelDetailsQueryRequest) (userRoleLevelDetailss []*response.UserRoleLevelDetailsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + userRoleLevelDetailss = append(userRoleLevelDetailss, mapper.UserRoleLevelDetailsResponseMapper(result)) + } + + return +} + +func (_i *userRoleLevelDetailsService) Show(id uint) (userRoleLevelDetails *response.UserRoleLevelDetailsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UserRoleLevelDetailsResponseMapper(result), nil +} + +func (_i *userRoleLevelDetailsService) Save(req request.UserRoleLevelDetailsCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + return _i.Repo.Create(req.ToEntity()) +} + +func (_i *userRoleLevelDetailsService) Update(id uint, req request.UserRoleLevelDetailsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *userRoleLevelDetailsService) Delete(id uint) error { + return _i.Repo.Delete(id) +} diff --git a/app/module/user_role_level_details/user_role_level_details.module.go b/app/module/user_role_level_details/user_role_level_details.module.go new file mode 100644 index 0000000..6a9f8d8 --- /dev/null +++ b/app/module/user_role_level_details/user_role_level_details.module.go @@ -0,0 +1,54 @@ +package user_role_level_details + +import ( + "narasi-ahli-be/app/module/user_role_level_details/controller" + "narasi-ahli-be/app/module/user_role_level_details/repository" + "narasi-ahli-be/app/module/user_role_level_details/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of UserRoleLevelDetailsRouter +type UserRoleLevelDetailsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of UserRoleLevelDetails module +var NewUserRoleLevelDetailsModule = fx.Options( + // register repository of UserRoleLevelDetails module + fx.Provide(repository.NewUserRoleLevelDetailsRepository), + + // register service of UserRoleLevelDetails module + fx.Provide(service.NewUserRoleLevelDetailsService), + + // register controller of UserRoleLevelDetails module + fx.Provide(controller.NewController), + + // register router of UserRoleLevelDetails module + fx.Provide(NewUserRoleLevelDetailsRouter), +) + +// init UserRoleLevelDetailsRouter +func NewUserRoleLevelDetailsRouter(fiber *fiber.App, controller *controller.Controller) *UserRoleLevelDetailsRouter { + return &UserRoleLevelDetailsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of UserRoleLevelDetails module +func (_i *UserRoleLevelDetailsRouter) RegisterUserRoleLevelDetailsRoutes() { + // define controllers + userRoleLevelDetailsController := _i.Controller.UserRoleLevelDetails + + // define routes + _i.App.Route("/user-role-level-details", func(router fiber.Router) { + router.Get("/", userRoleLevelDetailsController.All) + router.Get("/:id", userRoleLevelDetailsController.Show) + router.Post("/", userRoleLevelDetailsController.Save) + router.Put("/:id", userRoleLevelDetailsController.Update) + router.Delete("/:id", userRoleLevelDetailsController.Delete) + }) +} diff --git a/app/module/user_roles/controller/controller.go b/app/module/user_roles/controller/controller.go new file mode 100644 index 0000000..64f3084 --- /dev/null +++ b/app/module/user_roles/controller/controller.go @@ -0,0 +1,17 @@ +package controller + +import ( + "narasi-ahli-be/app/module/user_roles/service" + + "github.com/rs/zerolog" +) + +type Controller struct { + UserRoles UserRolesController +} + +func NewController(UserRolesService service.UserRolesService, log zerolog.Logger) *Controller { + return &Controller{ + UserRoles: NewUserRolesController(UserRolesService, log), + } +} diff --git a/app/module/user_roles/controller/user_roles.controller.go b/app/module/user_roles/controller/user_roles.controller.go new file mode 100644 index 0000000..5080ef6 --- /dev/null +++ b/app/module/user_roles/controller/user_roles.controller.go @@ -0,0 +1,199 @@ +package controller + +import ( + "narasi-ahli-be/app/module/user_roles/request" + "narasi-ahli-be/app/module/user_roles/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog" +) + +type userRolesController struct { + userRolesService service.UserRolesService + Log zerolog.Logger +} + +type UserRolesController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewUserRolesController(userRolesService service.UserRolesService, log zerolog.Logger) UserRolesController { + return &userRolesController{ + userRolesService: userRolesService, + Log: log, + } +} + +// All UserRoles +// @Summary Get all UserRoles +// @Description API for getting all UserRoles +// @Tags UserRoles +// @Security Bearer +// @Param req query request.UserRolesQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-roles [get] +func (_i *userRolesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.UserRolesQueryRequestContext{ + Name: c.Query("name"), + Description: c.Query("description"), + Code: c.Query("code"), + UserLevelId: c.Query("userLevelId"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + userRolesData, paging, err := _i.userRolesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoles list successfully retrieved"}, + Data: userRolesData, + Meta: paging, + }) +} + +// Show UserRoles +// @Summary Get one UserRoles +// @Description API for getting one UserRoles +// @Tags UserRoles +// @Security Bearer +// @Param id path int true "UserRoles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-roles/{id} [get] +func (_i *userRolesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + userRolesData, err := _i.userRolesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoles successfully retrieved"}, + Data: userRolesData, + }) +} + +// Save UserRoles +// @Summary Create UserRoles +// @Description API for create UserRoles +// @Tags UserRoles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.UserRolesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-roles [post] +func (_i *userRolesController) Save(c *fiber.Ctx) error { + req := new(request.UserRolesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + err := _i.userRolesService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoles successfully created"}, + }) +} + +// Update UserRoles +// @Summary update UserRoles +// @Description API for update UserRoles +// @Tags UserRoles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserRolesUpdateRequest true "Required payload" +// @Param id path int true "UserRoles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-roles/{id} [put] +func (_i *userRolesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UserRolesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.userRolesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoles successfully updated"}, + }) +} + +// Delete UserRoles +// @Summary delete UserRoles +// @Description API for delete UserRoles +// @Tags UserRoles +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "UserRoles ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-roles/{id} [delete] +func (_i *userRolesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.userRolesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserRoles successfully deleted"}, + }) +} diff --git a/app/module/user_roles/mapper/user_roles.mapper.go b/app/module/user_roles/mapper/user_roles.mapper.go new file mode 100644 index 0000000..6b1c1a1 --- /dev/null +++ b/app/module/user_roles/mapper/user_roles.mapper.go @@ -0,0 +1,24 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + res "narasi-ahli-be/app/module/user_roles/response" +) + +func UserRolesResponseMapper(userRolesReq *entity.UserRoles) (userRolesRes *res.UserRolesResponse) { + if userRolesReq != nil { + userRolesRes = &res.UserRolesResponse{ + ID: userRolesReq.ID, + Name: userRolesReq.Name, + Description: userRolesReq.Description, + Code: userRolesReq.Code, + UserLevelId: userRolesReq.UserLevelId, + StatusId: userRolesReq.StatusId, + CreatedById: userRolesReq.CreatedById, + IsActive: userRolesReq.IsActive, + CreatedAt: userRolesReq.CreatedAt, + UpdatedAt: userRolesReq.UpdatedAt, + } + } + return userRolesRes +} diff --git a/app/module/user_roles/repository/user_roles.repository.go b/app/module/user_roles/repository/user_roles.repository.go new file mode 100644 index 0000000..982bb96 --- /dev/null +++ b/app/module/user_roles/repository/user_roles.repository.go @@ -0,0 +1,106 @@ +package repository + +import ( + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/user_roles/request" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + + "github.com/rs/zerolog" +) + +type userRolesRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// UserRolesRepository define interface of IUserRolesRepository +type UserRolesRepository interface { + GetAll(req request.UserRolesQueryRequest) (userRoles []*entity.UserRoles, paging paginator.Pagination, err error) + FindOne(id uint) (userRoles *entity.UserRoles, err error) + Create(userRoles *entity.UserRoles) (userRolesReturn *entity.UserRoles, err error) + Update(id uint, userRoles *entity.UserRoles) (err error) + Delete(id uint) (err error) +} + +func NewUserRolesRepository(db *database.Database, log zerolog.Logger) UserRolesRepository { + return &userRolesRepository{ + DB: db, + Log: log, + } +} + +// implement interface of IUserRolesRepository +func (_i *userRolesRepository) GetAll(req request.UserRolesQueryRequest) (userRoles []*entity.UserRoles, paging paginator.Pagination, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + var count int64 + + query := _i.DB.DB.Model(&entity.UserRoles{}) + query = query.Where("is_active = ?", true) + + if req.Name != nil && *req.Name != "" { + name := strings.ToLower(*req.Name) + query = query.Where("LOWER(name) LIKE ?", "%"+strings.ToLower(name)+"%") + } + if req.Code != nil { + query = query.Where("code = ?", req.Code) + } + if req.UserLevelId != nil { + query = query.Where("user_level_id = ?", req.UserLevelId) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&userRoles).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *userRolesRepository) FindOne(id uint) (userRoles *entity.UserRoles, err error) { + if err := _i.DB.DB.First(&userRoles, id).Error; err != nil { + return nil, err + } + + return userRoles, nil +} + +func (_i *userRolesRepository) Create(userRoles *entity.UserRoles) (userRolesReturn *entity.UserRoles, err error) { + result := _i.DB.DB.Create(userRoles) + return userRoles, result.Error +} + +func (_i *userRolesRepository) Update(id uint, userRoles *entity.UserRoles) (err error) { + userRolesMap, err := utilSvc.StructToMap(userRoles) + if err != nil { + return err + } + return _i.DB.DB.Model(&entity.UserRoles{}). + Where(&entity.UserRoles{ID: id}). + Updates(userRolesMap).Error +} + +func (_i *userRolesRepository) Delete(id uint) error { + return _i.DB.DB.Delete(&entity.UserRoles{}, id).Error +} diff --git a/app/module/user_roles/request/user_roles.request.go b/app/module/user_roles/request/user_roles.request.go new file mode 100644 index 0000000..6ff92ac --- /dev/null +++ b/app/module/user_roles/request/user_roles.request.go @@ -0,0 +1,92 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + userRoleAccessReq "narasi-ahli-be/app/module/user_role_accesses/request" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type UserRolesGeneric interface { + ToEntity() +} + +type UserRolesQueryRequest struct { + Name *string `json:"name"` + Description *string `json:"description"` + Code *string `json:"code"` + UserLevelId *int `json:"userLevelId"` + StatusId *int `json:"statusId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type UserRolesCreateRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + Code string `json:"code" validate:"required"` + UserLevelIds []uint `json:"userLevelIds" validate:"required"` + UserRoleAccess []userRoleAccessReq.UserRoleAccessesCreateRequest `json:"userRoleAccess" validate:"required"` + StatusId int `json:"statusId" validate:"required"` +} + +func (req UserRolesCreateRequest) ToEntity() *entity.UserRoles { + return &entity.UserRoles{ + Name: req.Name, + Description: req.Description, + Code: req.Code, + StatusId: req.StatusId, + } +} + +type UserRolesUpdateRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + Code string `json:"code" validate:"required"` + LevelNumber int `json:"level_number" validate:"required"` + UserLevelIds []uint `json:"userLevelIds" validate:"required"` + StatusId int `json:"status_id" validate:"required"` +} + +func (req UserRolesUpdateRequest) ToEntity() *entity.UserRoles { + return &entity.UserRoles{ + Name: req.Name, + Description: req.Description, + Code: req.Code, + StatusId: req.StatusId, + UpdatedAt: time.Now(), + } +} + +type UserRolesQueryRequestContext struct { + Name string `json:"name"` + Description string `json:"description"` + Code string `json:"code"` + UserLevelId string `json:"userLevelId"` + StatusId string `json:"statusId"` +} + +func (req UserRolesQueryRequestContext) ToParamRequest() UserRolesQueryRequest { + var request UserRolesQueryRequest + + if name := req.Name; name != "" { + request.Name = &name + } + if code := req.Code; code != "" { + request.Code = &code + } + if userLevelIdStr := req.UserLevelId; userLevelIdStr != "" { + userLevelId, err := strconv.Atoi(userLevelIdStr) + if err == nil { + request.UserLevelId = &userLevelId + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/user_roles/response/user_roles.response.go b/app/module/user_roles/response/user_roles.response.go new file mode 100644 index 0000000..7747582 --- /dev/null +++ b/app/module/user_roles/response/user_roles.response.go @@ -0,0 +1,16 @@ +package response + +import "time" + +type UserRolesResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Code string `json:"code"` + UserLevelId uint `json:"user_level_id"` + StatusId int `json:"status_id"` + CreatedById *uint `json:"created_by_id"` + IsActive *bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/app/module/user_roles/service/user_roles.service.go b/app/module/user_roles/service/user_roles.service.go new file mode 100644 index 0000000..fb97a62 --- /dev/null +++ b/app/module/user_roles/service/user_roles.service.go @@ -0,0 +1,143 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity" + userLevelsRepository "narasi-ahli-be/app/module/user_levels/repository" + userRoleAccessRepository "narasi-ahli-be/app/module/user_role_accesses/repository" + userRoleLevelDetailsRepository "narasi-ahli-be/app/module/user_role_level_details/repository" + "narasi-ahli-be/app/module/user_roles/mapper" + "narasi-ahli-be/app/module/user_roles/repository" + "narasi-ahli-be/app/module/user_roles/request" + "narasi-ahli-be/app/module/user_roles/response" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + + "github.com/rs/zerolog" +) + +// UserRolesService +type userRolesService struct { + Repo repository.UserRolesRepository + UsersRepo usersRepository.UsersRepository + UserLevelsRepo userLevelsRepository.UserLevelsRepository + UserRoleLevelDetailsRepo userRoleLevelDetailsRepository.UserRoleLevelDetailsRepository + UserRoleAccessRepo userRoleAccessRepository.UserRoleAccessesRepository + Log zerolog.Logger +} + +// UserRolesService define interface of IUserRolesService +type UserRolesService interface { + All(req request.UserRolesQueryRequest) (userRoles []*response.UserRolesResponse, paging paginator.Pagination, err error) + Show(id uint) (userRoles *response.UserRolesResponse, err error) + Save(req request.UserRolesCreateRequest, authToken string) (err error) + Update(id uint, req request.UserRolesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewUserRolesService init UserRolesService +func NewUserRolesService( + repo repository.UserRolesRepository, + usersRepo usersRepository.UsersRepository, + userLevelsRepo userLevelsRepository.UserLevelsRepository, + userRoleLevelDetailsRepo userRoleLevelDetailsRepository.UserRoleLevelDetailsRepository, + userRoleAccessRepo userRoleAccessRepository.UserRoleAccessesRepository, + log zerolog.Logger, +) UserRolesService { + + return &userRolesService{ + Repo: repo, + UsersRepo: usersRepo, + UserLevelsRepo: userLevelsRepo, + UserRoleLevelDetailsRepo: userRoleLevelDetailsRepo, + UserRoleAccessRepo: userRoleAccessRepo, + Log: log, + } +} + +// All implement interface of UserRolesService +func (_i *userRolesService) All(req request.UserRolesQueryRequest) (userRoless []*response.UserRolesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + + if err != nil { + return + } + + for _, result := range results { + userRoless = append(userRoless, mapper.UserRolesResponseMapper(result)) + } + + return +} + +func (_i *userRolesService) Show(id uint) (userRoles *response.UserRolesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UserRolesResponseMapper(result), nil +} + +func (_i *userRolesService) Save(req request.UserRolesCreateRequest, authToken string) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + newReq.CreatedById = &createdBy.ID + + userRolesReturn, err := _i.Repo.Create(newReq) + if err == nil { + var userRoleAccessList []entity.UserRoleAccesses + for _, item := range req.UserRoleAccess { + userRoleAccess := entity.UserRoleAccesses{ + UserRoleId: userRolesReturn.ID, + MenuId: item.MenuId, + IsViewEnabled: item.IsViewEnabled, + IsInsertEnabled: item.IsInsertEnabled, + IsUpdateEnabled: item.IsUpdateEnabled, + IsDeleteEnabled: item.IsDeleteEnabled, + IsApprovalEnabled: item.IsApprovalEnabled, + IsAdminEnabled: item.IsAdminEnabled, + } + userRoleAccessList = append(userRoleAccessList, userRoleAccess) + } + err := _i.UserRoleAccessRepo.CreateAll(&userRoleAccessList) + if err != nil { + return err + } + + var UserRoleLevelDetailList []entity.UserRoleLevelDetails + isActive := true + for _, id := range req.UserLevelIds { + userRoleLevelDetail := entity.UserRoleLevelDetails{ + UserRoleId: userRolesReturn.ID, + UserLevelId: id, + IsActive: &isActive, + } + UserRoleLevelDetailList = append(UserRoleLevelDetailList, userRoleLevelDetail) + } + err = _i.UserRoleLevelDetailsRepo.CreateAll(&UserRoleLevelDetailList) + if err != nil { + return err + } + } + + return err +} + +func (_i *userRolesService) Update(id uint, req request.UserRolesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + return _i.Repo.Update(id, req.ToEntity()) +} + +func (_i *userRolesService) Delete(id uint) (err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} diff --git a/app/module/user_roles/user_roles.module.go b/app/module/user_roles/user_roles.module.go new file mode 100644 index 0000000..10f801b --- /dev/null +++ b/app/module/user_roles/user_roles.module.go @@ -0,0 +1,54 @@ +package user_roles + +import ( + "narasi-ahli-be/app/module/user_roles/controller" + "narasi-ahli-be/app/module/user_roles/repository" + "narasi-ahli-be/app/module/user_roles/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of UserRolesRouter +type UserRolesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of UserRoles module +var NewUserRolesModule = fx.Options( + // register repository of UserRoles module + fx.Provide(repository.NewUserRolesRepository), + + // register service of UserRoles module + fx.Provide(service.NewUserRolesService), + + // register controller of UserRoles module + fx.Provide(controller.NewController), + + // register router of UserRoles module + fx.Provide(NewUserRolesRouter), +) + +// init UserRolesRouter +func NewUserRolesRouter(fiber *fiber.App, controller *controller.Controller) *UserRolesRouter { + return &UserRolesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of UserRoles module +func (_i *UserRolesRouter) RegisterUserRolesRoutes() { + // define controllers + userRolesController := _i.Controller.UserRoles + + // define routes + _i.App.Route("/user-roles", func(router fiber.Router) { + router.Get("/", userRolesController.All) + router.Get("/:id", userRolesController.Show) + router.Post("/", userRolesController.Save) + router.Put("/:id", userRolesController.Update) + router.Delete("/:id", userRolesController.Delete) + }) +} diff --git a/app/module/users/controller/controller.go b/app/module/users/controller/controller.go new file mode 100644 index 0000000..0a544a1 --- /dev/null +++ b/app/module/users/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/users/service" + +type Controller struct { + Users UsersController +} + +func NewController(UsersService service.UsersService) *Controller { + return &Controller{ + Users: NewUsersController(UsersService), + } +} diff --git a/app/module/users/controller/users.controller.go b/app/module/users/controller/users.controller.go new file mode 100644 index 0000000..8a74be5 --- /dev/null +++ b/app/module/users/controller/users.controller.go @@ -0,0 +1,554 @@ +package controller + +import ( + "narasi-ahli-be/app/module/users/request" + "narasi-ahli-be/app/module/users/service" + "narasi-ahli-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + _ "github.com/gofiber/fiber/v2" + + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" +) + +type usersController struct { + usersService service.UsersService +} + +type UsersController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + ShowByUsername(c *fiber.Ctx) error + ShowInfo(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Login(c *fiber.Ctx) error + ParetoLogin(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + SavePassword(c *fiber.Ctx) error + ResetPassword(c *fiber.Ctx) error + ForgotPassword(c *fiber.Ctx) error + OtpRequest(c *fiber.Ctx) error + OtpValidation(c *fiber.Ctx) error + EmailValidation(c *fiber.Ctx) error + SetupEmail(c *fiber.Ctx) error +} + +func NewUsersController(usersService service.UsersService) UsersController { + return &usersController{ + usersService: usersService, + } +} + +// All Users +// @Summary Get all Users +// @Description API for getting all Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.UsersQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users [get] +func (_i *usersController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + reqContext := request.UsersQueryRequestContext{ + Username: c.Query("username"), + Email: c.Query("email"), + Fullname: c.Query("fullname"), + PhoneNumber: c.Query("phoneNumber"), + WorkType: c.Query("workType"), + GenderType: c.Query("genderType"), + IdentityType: c.Query("identityType"), + IdentityGroup: c.Query("identityGroup"), + IdentityGroupNumber: c.Query("identityGroupNumber"), + IdentityNumber: c.Query("identityNumber"), + UserRoleId: c.Query("userRoleId"), + StatusId: c.Query("statusId"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + usersData, paging, err := _i.usersService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users list successfully retrieved"}, + Data: usersData, + Meta: paging, + }) +} + +// Show Users +// @Summary Get one Users +// @Description API for getting one Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Users ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/detail/{id} [get] +func (_i *usersController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + usersData, err := _i.usersService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully retrieved"}, + Data: usersData, + }) +} + +// ShowByUsername Users +// @Summary Get one Users +// @Description API for getting one Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param username path string true "Username" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/username/{username} [get] +func (_i *usersController) ShowByUsername(c *fiber.Ctx) error { + username := c.Params("username") + + usersData, err := _i.usersService.ShowByUsername(username) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully retrieved"}, + Data: usersData, + }) +} + +// ShowInfo Users +// @Summary ShowInfo Users +// @Description API for ShowUserInfo +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/info [get] +func (_i *usersController) ShowInfo(c *fiber.Ctx) error { + authToken := c.Get("Authorization") + + dataResult, err := _i.usersService.ShowUserInfo(authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully retrieve"}, + Data: dataResult, + }) +} + +// Save Users +// @Summary Create Users +// @Description API for create Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.UsersCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users [post] +func (_i *usersController) Save(c *fiber.Ctx) error { + req := new(request.UsersCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + dataResult, err := _i.usersService.Save(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully created"}, + Data: dataResult, + }) +} + +// Update Users +// @Summary update Users +// @Description API for update Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Users ID" +// @Param payload body request.UsersUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/{id} [put] +func (_i *usersController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UsersUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.usersService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully updated"}, + }) +} + +// Login Users +// @Summary Login Users +// @Description API for Login Users +// @Tags Users +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserLogin true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/login [post] +func (_i *usersController) Login(c *fiber.Ctx) error { + req := new(request.UserLogin) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + loginResponse, err := _i.usersService.Login(*req) + if err != nil { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Code: 401, + Messages: utilRes.Messages{err.Error()}, + }) + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully login"}, + Data: loginResponse, + }) +} + +// ParetoLogin Users +// @Summary ParetoLogin Users +// @Description API for ParetoLogin Users +// @Tags Users +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserLogin true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/pareto-login [post] +func (_i *usersController) ParetoLogin(c *fiber.Ctx) error { + req := new(request.UserLogin) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + loginResponse, err := _i.usersService.ParetoLogin(*req) + if err != nil { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Code: 401, + Messages: utilRes.Messages{err.Error()}, + }) + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully login"}, + Data: loginResponse, + }) +} + +// Delete Users +// @Summary delete Users +// @Description API for delete Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Users ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/{id} [delete] +func (_i *usersController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.usersService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users successfully deleted"}, + }) +} + +// SavePassword Users +// @Summary SavePassword Users +// @Description API for SavePassword Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.UserSavePassword true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/save-password [post] +func (_i *usersController) SavePassword(c *fiber.Ctx) error { + req := new(request.UserSavePassword) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authToken := c.Get("Authorization") + + err := _i.usersService.SavePassword(*req, authToken) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users password successfully update"}, + }) +} + +// ResetPassword Users +// @Summary ResetPassword Users +// @Description API for ResetPassword Users +// @Tags Users +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserResetPassword true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/reset-password [post] +func (_i *usersController) ResetPassword(c *fiber.Ctx) error { + req := new(request.UserResetPassword) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.ResetPassword(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users password successfully reset"}, + }) +} + +// ForgotPassword Users +// @Summary ForgotPassword Users +// @Description API for ForgotPassword Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserForgotPassword true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/forgot-password [post] +func (_i *usersController) ForgotPassword(c *fiber.Ctx) error { + req := new(request.UserForgotPassword) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.ForgotPassword(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Users forgot password has sent"}, + }) +} + +// OtpRequest Users +// @Summary OtpRequest Users +// @Description API for OtpRequest Users +// @Tags Users +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserOtpRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/otp-request [post] +func (_i *usersController) OtpRequest(c *fiber.Ctx) error { + req := new(request.UserOtpRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.OtpRequest(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Otp has sent"}, + }) +} + +// OtpValidation Users +// @Summary OtpValidation Users +// @Description API for OtpValidation Users +// @Tags Users +// @Security Bearer +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserOtpValidation true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/otp-validation [post] +func (_i *usersController) OtpValidation(c *fiber.Ctx) error { + req := new(request.UserOtpValidation) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.usersService.OtpValidation(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"OTP is valid"}, + }) +} + +// EmailValidation Users +// @Summary EmailValidation Users +// @Description API for Email Validation Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserEmailValidationRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/email-validation [post] +func (_i *usersController) EmailValidation(c *fiber.Ctx) error { + req := new(request.UserEmailValidationRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + messageResponse, err := _i.usersService.EmailValidationPreLogin(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{messageResponse}, + }) +} + +// SetupEmail Users +// @Summary SetupEmail Users +// @Description API for Setup Email Users +// @Tags Users +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param payload body request.UserEmailValidationRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /users/setup-email [post] +func (_i *usersController) SetupEmail(c *fiber.Ctx) error { + req := new(request.UserEmailValidationRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + messageResponse, err := _i.usersService.SetupEmail(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{messageResponse}, + }) +} diff --git a/app/module/users/mapper/users.mapper.go b/app/module/users/mapper/users.mapper.go new file mode 100644 index 0000000..c044832 --- /dev/null +++ b/app/module/users/mapper/users.mapper.go @@ -0,0 +1,43 @@ +package mapper + +import ( + "narasi-ahli-be/app/database/entity/users" + userLevelsRepository "narasi-ahli-be/app/module/user_levels/repository" + res "narasi-ahli-be/app/module/users/response" +) + +func UsersResponseMapper(usersReq *users.Users, userLevelsRepo userLevelsRepository.UserLevelsRepository) (usersRes *res.UsersResponse) { + if usersReq != nil { + findUserLevel, _ := userLevelsRepo.FindOne(usersReq.UserLevelId) + userLevelGroup := "" + if findUserLevel != nil { + userLevelGroup = findUserLevel.AliasName + } + + usersRes = &res.UsersResponse{ + ID: usersReq.ID, + Username: usersReq.Username, + Email: usersReq.Email, + Fullname: usersReq.Fullname, + Address: usersReq.Address, + PhoneNumber: usersReq.PhoneNumber, + WorkType: usersReq.WorkType, + GenderType: usersReq.GenderType, + IdentityType: usersReq.IdentityType, + IdentityNumber: usersReq.IdentityNumber, + DateOfBirth: usersReq.DateOfBirth, + LastEducation: usersReq.LastEducation, + KeycloakId: usersReq.KeycloakId, + UserRoleId: usersReq.UserRoleId, + UserLevelGroup: userLevelGroup, + StatusId: usersReq.StatusId, + UserLevelId: usersReq.UserLevelId, + CreatedById: usersReq.CreatedById, + ProfilePicturePath: usersReq.ProfilePicturePath, + IsActive: usersReq.IsActive, + CreatedAt: usersReq.CreatedAt, + UpdatedAt: usersReq.UpdatedAt, + } + } + return usersRes +} diff --git a/app/module/users/repository/users.repository.go b/app/module/users/repository/users.repository.go new file mode 100644 index 0000000..7721366 --- /dev/null +++ b/app/module/users/repository/users.repository.go @@ -0,0 +1,220 @@ +package repository + +import ( + "encoding/json" + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/database/entity/users" + "narasi-ahli-be/app/module/users/request" + "narasi-ahli-be/utils/paginator" + "strings" + + "github.com/rs/zerolog" +) + +type usersRepository struct { + DB *database.Database + Log zerolog.Logger +} + +// UsersRepository define interface of IUsersRepository +type UsersRepository interface { + GetAll(req request.UsersQueryRequest) (userss []*users.Users, paging paginator.Pagination, err error) + FindOne(id uint) (users *users.Users, err error) + FindByKeycloakId(keycloakId string) (users *users.Users, err error) + FindByUsername(username string) (users *users.Users, err error) + Create(users *users.Users) (userReturn *users.Users, err error) + Update(id uint, users *users.Users) (err error) + Delete(id uint) (err error) + CreateForgotPassword(forgotPasswords *entity.ForgotPasswords) (err error) + UpdateForgotPassword(id uint, forgotPasswords *entity.ForgotPasswords) (err error) + FindForgotPassword(keycloakId string, code string) (forgotPasswords *entity.ForgotPasswords, err error) + CreateOtp(otp *entity.OneTimePasswords) (err error) + FindOtpByEmail(email string, code string) (otp *entity.OneTimePasswords, err error) + FindOtpByIdentity(identity string, code string) (otp *entity.OneTimePasswords, err error) +} + +func NewUsersRepository(db *database.Database, log zerolog.Logger) UsersRepository { + return &usersRepository{ + DB: db, + Log: log, + } +} + +// implement interface of IUsersRepository +func (_i *usersRepository) GetAll(req request.UsersQueryRequest) (userss []*users.Users, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&users.Users{}) + + query = query.Where("is_active = ?", true) + + if req.Username != nil && *req.Username != "" { + username := strings.ToLower(*req.Username) + query = query.Where("LOWER(username) LIKE ?", "%"+strings.ToLower(username)+"%") + } + if req.Fullname != nil && *req.Fullname != "" { + fullname := strings.ToLower(*req.Fullname) + query = query.Where("LOWER(fullname) LIKE ?", "%"+strings.ToLower(fullname)+"%") + } + if req.Email != nil && *req.Email != "" { + query = query.Where("email = ?", req.Email) + } + if req.PhoneNumber != nil && *req.PhoneNumber != "" { + query = query.Where("phone_number = ?", req.PhoneNumber) + } + if req.WorkType != nil && *req.WorkType != "" { + query = query.Where("work_type = ?", req.WorkType) + } + if req.GenderType != nil && *req.GenderType != "" { + query = query.Where("gender_type = ?", req.GenderType) + } + if req.IdentityType != nil && *req.IdentityType != "" { + query = query.Where("identity_type = ?", req.IdentityType) + } + if req.IdentityGroup != nil && *req.IdentityGroup != "" { + query = query.Where("identity_group = ?", req.IdentityGroup) + } + if req.IdentityGroupNumber != nil && *req.IdentityGroupNumber != "" { + query = query.Where("identity_group_number = ?", req.IdentityGroupNumber) + } + if req.IdentityNumber != nil && *req.IdentityNumber != "" { + query = query.Where("identity_number = ?", req.IdentityNumber) + } + if req.UserRoleId != nil { + query = query.Where("user_role_id = ?", req.UserRoleId) + } + if req.StatusId != nil { + query = query.Where("status_id = ?", req.StatusId) + } + query.Count(&count) + + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&userss).Error + if err != nil { + return + } + + paging = *req.Pagination + + return +} + +func (_i *usersRepository) FindOne(id uint) (users *users.Users, err error) { + query := _i.DB.DB.Where("id = ?", id) + + if err := query.Preload("UserLevel").First(&users).Error; err != nil { + return nil, err + } + + return users, nil +} + +func (_i *usersRepository) FindByKeycloakId(keycloakId string) (users *users.Users, err error) { + query := _i.DB.DB.Where("keycloak_id = ?", keycloakId) + if err := query.Preload("UserLevel").First(&users).Error; err != nil { + return nil, err + } + + return users, nil +} + +func (_i *usersRepository) FindByUsername(username string) (users *users.Users, err error) { + query := _i.DB.DB.Where("username = ?", username) + + if err := query.Preload("UserLevel").First(&users).Error; err != nil { + return nil, err + } + + return users, nil +} + +func (_i *usersRepository) Create(users *users.Users) (userReturn *users.Users, err error) { + result := _i.DB.DB.Create(users) + return users, result.Error +} + +func (_i *usersRepository) Update(id uint, userReturn *users.Users) (err error) { + userReturnMap, err := StructToMap(userReturn) + delete(userReturnMap, "user_levels") + _i.Log.Info().Interface("Update", userReturnMap).Msg("") + if err != nil { + return err + } + query := _i.DB.DB.Model(&users.Users{}).Where(&users.Users{ID: id}) + return query.Updates(userReturnMap).Error +} + +func (_i *usersRepository) Delete(id uint) error { + query := _i.DB.DB.Model(&users.Users{}).Where("id = ?", id) + return query.Delete(&users.Users{}).Error +} + +func (_i *usersRepository) CreateForgotPassword(forgotPasswords *entity.ForgotPasswords) (err error) { + result := _i.DB.DB.Create(forgotPasswords) + return result.Error +} + +func (_i *usersRepository) UpdateForgotPassword(id uint, forgotPasswords *entity.ForgotPasswords) (err error) { + _i.Log.Info().Interface("forgotPasswords", id).Msg("") + _i.Log.Info().Interface("forgotPasswords", forgotPasswords).Msg("") + + return _i.DB.DB.Model(&entity.ForgotPasswords{}). + Where(&entity.ForgotPasswords{ID: id}). + Updates(forgotPasswords).Error +} + +func (_i *usersRepository) FindForgotPassword(keycloakId string, code string) (forgotPasswords *entity.ForgotPasswords, err error) { + if err := _i.DB.DB.Where("keycloak_id = ?", keycloakId).Where("code_request = ?", code).Where("is_active = ?", true).First(&forgotPasswords).Error; err != nil { + return nil, err + } + + return forgotPasswords, nil +} + +func (_i *usersRepository) CreateOtp(otp *entity.OneTimePasswords) (err error) { + result := _i.DB.DB.Create(otp) + return result.Error +} + +func (_i *usersRepository) FindOtpByEmail(email string, code string) (otp *entity.OneTimePasswords, err error) { + if err := _i.DB.DB.Where("email = ?", email).Where("otp_code = ?", code).First(&otp).Error; err != nil { + return nil, err + } + + return otp, nil +} + +func (_i *usersRepository) FindOtpByIdentity(identity string, code string) (otp *entity.OneTimePasswords, err error) { + if err := _i.DB.DB.Where("identity = ?", identity).Where("otp_code = ?", code).First(&otp).Error; err != nil { + return nil, err + } + + return otp, nil +} + +func StructToMap(obj interface{}) (map[string]interface{}, error) { + var result map[string]interface{} + jsonData, err := json.Marshal(obj) + if err != nil { + return nil, err + } + + err = json.Unmarshal(jsonData, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/app/module/users/request/users.request.go b/app/module/users/request/users.request.go new file mode 100644 index 0000000..191fbd3 --- /dev/null +++ b/app/module/users/request/users.request.go @@ -0,0 +1,224 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity/users" + "narasi-ahli-be/utils/paginator" + "strconv" + "time" +) + +type UsersGeneric interface { + ToEntity() +} + +type UsersQueryRequest struct { + Username *string `json:"username"` + Email *string `json:"email"` + Fullname *string `json:"fullname"` + PhoneNumber *string `json:"phoneNumber"` + WorkType *string `json:"workType"` + GenderType *string `json:"genderType"` + IdentityType *string `json:"identityType"` + IdentityGroup *string `json:"identityGroup"` + IdentityGroupNumber *string `json:"identityGroupNumber"` + IdentityNumber *string `json:"identityNumber"` + UserRoleId *int `json:"userRoleId"` + StatusId *int `json:"statusId"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type UsersCreateRequest struct { + Username string `json:"username" validate:"required,lowercase"` + Email string `json:"email" validate:"required,email"` + Fullname string `json:"fullname" validate:"required"` + UserLevelId uint `json:"userLevelId" validate:"required"` + UserRoleId uint `json:"userRoleId" validate:"required"` + Password string `json:"password" validate:"required"` + PhoneNumber *string `json:"phoneNumber"` + Address *string `json:"address"` + WorkType *string `json:"workType"` + GenderType *string `json:"genderType"` + IdentityType *string `json:"identityType"` + IdentityGroup *string `json:"identityGroup"` + IdentityGroupNumber *string `json:"identityGroupNumber"` + IdentityNumber *string `json:"identityNumber"` + DateOfBirth *string `json:"dateOfBirth"` + LastEducation *string `json:"lastEducation"` + Degree *string `json:"degree"` + WhatsappNumber *string `json:"whatsappNumber"` + LastJobTitle *string `json:"lastJobTitle"` +} + +func (req UsersCreateRequest) ToEntity() *users.Users { + return &users.Users{ + Username: req.Username, + Email: req.Email, + Fullname: req.Fullname, + Address: req.Address, + PhoneNumber: req.PhoneNumber, + WorkType: req.WorkType, + GenderType: req.GenderType, + IdentityType: req.IdentityType, + IdentityGroup: req.IdentityGroup, + IdentityGroupNumber: req.IdentityGroupNumber, + IdentityNumber: req.IdentityNumber, + DateOfBirth: req.DateOfBirth, + LastEducation: req.LastEducation, + Degree: req.Degree, + WhatsappNumber: req.WhatsappNumber, + LastJobTitle: req.LastJobTitle, + UserRoleId: req.UserRoleId, + UserLevelId: req.UserLevelId, + } +} + +type UsersUpdateRequest struct { + Username string `json:"username" validate:"required,lowercase"` + Email string `json:"email" validate:"required,email"` + Fullname string `json:"fullname" validate:"required"` + UserLevelId uint `json:"userLevelId" validate:"required"` + UserRoleId uint `json:"userRoleId" validate:"required"` + PhoneNumber *string `json:"phoneNumber"` + Address *string `json:"address"` + WorkType *string `json:"workType"` + GenderType *string `json:"genderType"` + IdentityType *string `json:"identityType"` + IdentityGroup *string `json:"identityGroup"` + IdentityGroupNumber *string `json:"identityGroupNumber"` + IdentityNumber *string `json:"identityNumber"` + DateOfBirth *string `json:"dateOfBirth"` + LastEducation *string `json:"lastEducation"` + Degree *string `json:"degree"` + WhatsappNumber *string `json:"whatsappNumber"` + LastJobTitle *string `json:"lastJobTitle"` + StatusId *int `json:"statusId"` +} + +func (req UsersUpdateRequest) ToEntity() *users.Users { + return &users.Users{ + Username: req.Username, + Email: req.Email, + Fullname: req.Fullname, + Address: req.Address, + PhoneNumber: req.PhoneNumber, + WorkType: req.WorkType, + GenderType: req.GenderType, + IdentityType: req.IdentityType, + IdentityGroup: req.IdentityGroup, + IdentityGroupNumber: req.IdentityGroupNumber, + IdentityNumber: req.IdentityNumber, + DateOfBirth: req.DateOfBirth, + LastEducation: req.LastEducation, + Degree: req.Degree, + WhatsappNumber: req.WhatsappNumber, + LastJobTitle: req.LastJobTitle, + UserRoleId: req.UserRoleId, + StatusId: req.StatusId, + UserLevelId: req.UserLevelId, + UpdatedAt: time.Now(), + } +} + +type UserLogin struct { + Username *string `json:"username"` + Password *string `json:"password"` + RefreshToken *string `json:"refreshToken"` +} + +type UserForgotPassword struct { + Username string `json:"username"` +} + +type UserSavePassword struct { + Password string `json:"password"` + ConfirmPassword string `json:"confirmPassword"` +} + +type UserResetPassword struct { + CodeRequest string `json:"codeRequest"` + UserId string `json:"userId"` + Password string `json:"password"` + ConfirmPassword string `json:"confirmPassword"` +} + +type UserEmailValidationRequest struct { + Username *string `json:"username"` + Password *string `json:"password"` + OldEmail *string `json:"oldEmail"` + NewEmail *string `json:"newEmail"` +} + +type UserOtpRequest struct { + Email string `json:"email" validate:"required,email"` + Name *string `json:"name"` +} + +type UserOtpValidation struct { + Email *string `json:"email"` + Username *string `json:"username"` + OtpCode string `json:"otpCode"` +} + +type UsersQueryRequestContext struct { + Username string `json:"username"` + Email string `json:"email"` + Fullname string `json:"fullname"` + PhoneNumber string `json:"phoneNumber"` + WorkType string `json:"workType"` + GenderType string `json:"genderType"` + IdentityType string `json:"identityType"` + IdentityGroup string `json:"identityGroup"` + IdentityGroupNumber string `json:"identityGroupNumber"` + IdentityNumber string `json:"identityNumber"` + UserRoleId string `json:"userRoleId"` + StatusId string `json:"statusId"` +} + +func (req UsersQueryRequestContext) ToParamRequest() UsersQueryRequest { + var request UsersQueryRequest + + if username := req.Username; username != "" { + request.Username = &username + } + if email := req.Email; email != "" { + request.Email = &email + } + if fullname := req.Fullname; fullname != "" { + request.Fullname = &fullname + } + if phoneNumber := req.PhoneNumber; phoneNumber != "" { + request.PhoneNumber = &phoneNumber + } + if workType := req.WorkType; workType != "" { + request.WorkType = &workType + } + if genderType := req.GenderType; genderType != "" { + request.GenderType = &genderType + } + if identityType := req.IdentityType; identityType != "" { + request.IdentityType = &identityType + } + if identityGroup := req.IdentityGroup; identityGroup != "" { + request.IdentityGroup = &identityGroup + } + if identityGroupNumber := req.IdentityGroupNumber; identityGroupNumber != "" { + request.IdentityGroupNumber = &identityGroupNumber + } + if identityNumber := req.IdentityNumber; identityNumber != "" { + request.IdentityNumber = &identityNumber + } + if userRoleIdStr := req.UserRoleId; userRoleIdStr != "" { + userRoleId, err := strconv.Atoi(userRoleIdStr) + if err == nil { + request.UserRoleId = &userRoleId + } + } + if statusIdStr := req.StatusId; statusIdStr != "" { + statusId, err := strconv.Atoi(statusIdStr) + if err == nil { + request.StatusId = &statusId + } + } + + return request +} diff --git a/app/module/users/response/users.response.go b/app/module/users/response/users.response.go new file mode 100644 index 0000000..2b4c309 --- /dev/null +++ b/app/module/users/response/users.response.go @@ -0,0 +1,36 @@ +package response + +import "time" + +type UsersResponse struct { + ID uint `json:"id"` + Username string `json:"username"` + Email string `json:"email"` + Fullname string `json:"fullname"` + Address *string `json:"address"` + PhoneNumber *string `json:"phoneNumber"` + WorkType *string `json:"workType"` + GenderType *string `json:"genderType"` + IdentityType *string `json:"identityType"` + IdentityNumber *string `json:"identityNumber"` + DateOfBirth *string `json:"dateOfBirth"` + LastEducation *string `json:"lastEducation"` + KeycloakId *string `json:"keycloakId"` + UserRoleId uint `json:"userRoleId"` + UserLevelId uint `json:"userLevelId"` + UserLevelGroup string `json:"userLevelGroup"` + StatusId *int `json:"statusId"` + CreatedById *uint `json:"createdById"` + ProfilePicturePath *string `json:"profilePicturePath"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type ParetoLoginResponse struct { + AccessToken string `json:"accessToken"` +} + +type VisitorStatistic struct { + TotalVisitor string `json:"accessToken"` +} diff --git a/app/module/users/service/users.service.go b/app/module/users/service/users.service.go new file mode 100644 index 0000000..3d4321c --- /dev/null +++ b/app/module/users/service/users.service.go @@ -0,0 +1,554 @@ +package service + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/database/entity/users" + userLevelsRepository "narasi-ahli-be/app/module/user_levels/repository" + "narasi-ahli-be/app/module/users/mapper" + "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/app/module/users/request" + "narasi-ahli-be/app/module/users/response" + "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + "strings" + "time" + + paseto "aidanwoods.dev/go-paseto" + "github.com/Nerzal/gocloak/v13" + "github.com/rs/zerolog" +) + +// UsersService +type usersService struct { + Repo repository.UsersRepository + UserLevelsRepo userLevelsRepository.UserLevelsRepository + Log zerolog.Logger + Keycloak *config.KeycloakConfig + Smtp *config.SmtpConfig +} + +// UsersService define interface of IUsersService +type UsersService interface { + All(req request.UsersQueryRequest) (users []*response.UsersResponse, paging paginator.Pagination, err error) + Show(id uint) (users *response.UsersResponse, err error) + ShowByUsername(username string) (users *response.UsersResponse, err error) + ShowUserInfo(authToken string) (users *response.UsersResponse, err error) + Save(req request.UsersCreateRequest, authToken string) (userReturn *users.Users, err error) + Login(req request.UserLogin) (res *gocloak.JWT, err error) + ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error) + Update(id uint, req request.UsersUpdateRequest) (err error) + Delete(id uint) error + SavePassword(req request.UserSavePassword, authToken string) (err error) + ResetPassword(req request.UserResetPassword) (err error) + ForgotPassword(req request.UserForgotPassword) (err error) + EmailValidationPreLogin(req request.UserEmailValidationRequest) (msgResponse *string, err error) + SetupEmail(req request.UserEmailValidationRequest) (msgResponse *string, err error) + OtpRequest(req request.UserOtpRequest) (err error) + OtpValidation(req request.UserOtpValidation) (err error) + SendLoginOtp(name string, email string, otp string) error + SendRegistrationOtp(name string, email string, otp string) error +} + +// NewUsersService init UsersService +func NewUsersService(repo repository.UsersRepository, userLevelsRepo userLevelsRepository.UserLevelsRepository, log zerolog.Logger, keycloak *config.KeycloakConfig, smtp *config.SmtpConfig) UsersService { + + return &usersService{ + Repo: repo, + UserLevelsRepo: userLevelsRepo, + Log: log, + Keycloak: keycloak, + Smtp: smtp, + } +} + +// All implement interface of UsersService +func (_i *usersService) All(req request.UsersQueryRequest) (users []*response.UsersResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + users = append(users, mapper.UsersResponseMapper(result, _i.UserLevelsRepo)) + } + + return +} + +func (_i *usersService) Show(id uint) (users *response.UsersResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UsersResponseMapper(result, _i.UserLevelsRepo), nil +} + +func (_i *usersService) ShowByUsername(username string) (users *response.UsersResponse, err error) { + result, err := _i.Repo.FindByUsername(username) + if err != nil { + return nil, err + } + + return mapper.UsersResponseMapper(result, _i.UserLevelsRepo), nil +} + +func (_i *usersService) ShowUserInfo(authToken string) (users *response.UsersResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken) + + return mapper.UsersResponseMapper(userInfo, _i.UserLevelsRepo), nil +} + +func (_i *usersService) Save(req request.UsersCreateRequest, authToken string) (userReturn *users.Users, err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + _i.Log.Info().Interface("AUTH TOKEN", authToken).Msg("") + + if authToken != "" { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken) + newReq.CreatedById = &createdBy.ID + } + + keycloakId, err := _i.Keycloak.CreateUser(req.Fullname, req.Email, req.Username, req.Password) + if err != nil { + return nil, err + } + + newReq.KeycloakId = &keycloakId + newReq.TempPassword = &req.Password + + return _i.Repo.Create(newReq) +} + +func (_i *usersService) Login(req request.UserLogin) (res *gocloak.JWT, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + var loginResponse *gocloak.JWT + if req.RefreshToken == nil { + loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password) + } else { + loginResponse, err = _i.Keycloak.RefreshToken(*req.RefreshToken) + } + if err != nil { + return nil, err + } + return loginResponse, nil +} + +func (_i *usersService) ParetoLogin(req request.UserLogin) (res *response.ParetoLoginResponse, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + var loginResponse *gocloak.JWT + token := paseto.NewToken() + secretKeyHex := "bdc42b1a0ba2bac3e27ba84241f9de06dee71b70f838af8d1beb0417f03d1d00" + secretKey, _ := paseto.V4SymmetricKeyFromHex(secretKeyHex) + // secretKey := paseto.NewV4SymmetricKey() // to change the secretKey periodically + + if req.RefreshToken == nil { + loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password) + } else { + // Retrieve Refresh Token + parser := paseto.NewParser() + verifiedToken, err := parser.ParseV4Local(secretKey, *req.RefreshToken, nil) + if err != nil { + panic(err) + } + + refreshToken, _ := verifiedToken.GetString("refresh_token") + _i.Log.Info().Interface("Pareto parse refresh token", refreshToken).Msg("") + + loginResponse, err = _i.Keycloak.RefreshToken(refreshToken) + } + + _i.Log.Info().Interface("loginResponse", loginResponse).Msg("") + + if err != nil { + return nil, err + } + + parseToken, err := ParseJWTToken(loginResponse.AccessToken) + if err != nil { + return nil, err + } + + issuedAt := parseToken["iat"].(float64) + expirationTime := parseToken["exp"].(float64) + issuer := parseToken["iss"].(string) + jti := parseToken["jti"].(string) + subject := parseToken["sub"].(string) + + token.SetIssuedAt(time.Unix(int64(issuedAt), 0)) + token.SetNotBefore(time.Unix(int64(issuedAt), 0)) + token.SetExpiration(time.Unix(int64(expirationTime), 0)) + token.SetIssuer(issuer) + token.SetJti(jti) + token.SetSubject(subject) + token.SetString("access_token", loginResponse.AccessToken) + token.SetString("refresh_token", loginResponse.RefreshToken) + + _i.Log.Info().Interface("Pareto Generated Key", secretKey.ExportHex()).Msg("") + + tokenEncrypted := token.V4Encrypt(secretKey, nil) + + parser := paseto.NewParser() + verifiedToken, err := parser.ParseV4Local(secretKey, tokenEncrypted, nil) + if err != nil { + panic(err) + } + + tokenParsing, _ := verifiedToken.GetString("access_token") + _i.Log.Info().Interface("Pareto parse token", tokenParsing).Msg("") + + resLogin := &response.ParetoLoginResponse{ + AccessToken: tokenEncrypted, + } + + if err != nil { + return nil, err + } + + return resLogin, nil +} + +func (_i *usersService) Update(id uint, req request.UsersUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + newReq := req.ToEntity() + + findUser, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + err = _i.Keycloak.UpdateUser(findUser.KeycloakId, req.Fullname, req.Email) + if err != nil { + return err + } + + return _i.Repo.Update(id, newReq) +} + +func (_i *usersService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} + +func (_i *usersService) SavePassword(req request.UserSavePassword, authToken string) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + _i.Log.Info().Interface("AUTH TOKEN", authToken).Msg("") + + if authToken != "" { + createdBy := utilSvc.GetUserInfo(_i.Log, _i.Repo, authToken) + + tokenString := strings.TrimPrefix(authToken, "Bearer ") + + err := _i.Keycloak.SetPassword(tokenString, *createdBy.KeycloakId, req.Password) + if err != nil { + return err + } + + return nil + } else { + return fmt.Errorf("Invalid token") + } +} + +func (_i *usersService) ResetPassword(req request.UserResetPassword) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + if req.Password != req.ConfirmPassword { + return fmt.Errorf("Invalid Password") + } + + user, err := _i.Repo.FindByKeycloakId(req.UserId) + if err != nil { + return fmt.Errorf("User Id Not Found") + } + + forgotPassword, err := _i.Repo.FindForgotPassword(req.UserId, req.CodeRequest) + if err != nil { + return fmt.Errorf("Invalid Request") + } + + _i.Log.Info().Interface("data", forgotPassword).Msg("") + + _i.Log.Info().Interface("dataForgotPassword", forgotPassword).Msg("") + + if user != nil { + err := _i.Keycloak.SetPasswordWithoutToken(req.UserId, req.Password) + if err != nil { + return err + } + + forgotPassword.IsActive = false + forgotPassword.UpdatedAt = time.Now() + err = _i.Repo.UpdateForgotPassword(forgotPassword.ID, forgotPassword) + if err != nil { + return err + } + + return nil + } else { + return fmt.Errorf("User not found") + } +} + +func (_i *usersService) ForgotPassword(req request.UserForgotPassword) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + user, err := _i.Repo.FindByUsername(req.Username) + if err != nil { + return err + } + + if user != nil { + codeRequest, err := utilSvc.GenerateNumericCode(8) + if err != nil { + return nil + } + forgotPasswordReq := entity.ForgotPasswords{ + KeycloakID: *user.KeycloakId, + CodeRequest: codeRequest, + IsActive: true, + } + + err = _i.Repo.CreateForgotPassword(&forgotPasswordReq) + if err != nil { + return err + } + + // send email forgot password + url := fmt.Sprintf("https://kontenhumas.com/setup-password?userId=%s&code=%s", *user.KeycloakId, codeRequest) + + subject := "[HUMAS POLRI] Forgot Password" + htmlBody := "

Anda telah mengirimkan permintaan untuk melakukan reset password.

" + htmlBody += "

Silahkan buat password akun anda dengan menekan tombol di bawah ini, untuk membuat password baru

" + htmlBody += fmt.Sprintf("Reset Password", url) + htmlBody += "

Terimakasih.

" + err = _i.Smtp.SendEmail(subject, user.Email, user.Fullname, htmlBody) + + return nil + } else { + return fmt.Errorf("User not found") + } +} + +func (_i *usersService) OtpRequest(req request.UserOtpRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + codeRequest, err := utilSvc.GenerateNumericCode(6) + if req.Name == nil { + req.Name = &req.Email + } + if err != nil { + return err + } + otpReq := entity.OneTimePasswords{ + Email: req.Email, + Name: req.Name, + OtpCode: codeRequest, + IsActive: true, + } + + err = _i.Repo.CreateOtp(&otpReq) + if err != nil { + return err + } + + err = _i.SendRegistrationOtp(*req.Name, req.Email, codeRequest) + if err != nil { + return err + } + + return nil +} + +func (_i *usersService) OtpValidation(req request.UserOtpValidation) (err error) { + _i.Log.Info().Interface("data", req).Msg("") + + var otp *entity.OneTimePasswords + if req.Email == nil { + otp, err = _i.Repo.FindOtpByIdentity(*req.Username, req.OtpCode) + if err != nil { + return fmt.Errorf("OTP is not valid") + } + } else { + otp, err = _i.Repo.FindOtpByEmail(*req.Email, req.OtpCode) + if err != nil { + return fmt.Errorf("OTP is not valid") + } + } + + if otp != nil { + if otp.ValidUntil.Before(time.Now()) { + return fmt.Errorf("OTP has expired") + } + + return nil + } else { + return fmt.Errorf("OTP is not valid") + } +} + +func (_i *usersService) EmailValidationPreLogin(req request.UserEmailValidationRequest) (msgResponse *string, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + var loginResponse *gocloak.JWT + loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password) + + if loginResponse == nil || err != nil { + return nil, fmt.Errorf("username / password incorrect") + } + + findUser, err := _i.Repo.FindByUsername(*req.Username) + if findUser == nil || err != nil { + return nil, fmt.Errorf("username / password incorrect") + } + + _i.Log.Info().Interface("data user", findUser).Msg("") + + if *findUser.IsEmailUpdated != true { + message := "Continue to setup email" + msgResponse = &message + } else { + codeRequest, err := utilSvc.GenerateNumericCode(6) + if err != nil { + return nil, err + } + otpReq := entity.OneTimePasswords{ + Email: findUser.Email, + Identity: &findUser.Username, + OtpCode: codeRequest, + IsActive: true, + } + + err = _i.Repo.CreateOtp(&otpReq) + if err != nil { + return nil, err + } + + err = _i.SendLoginOtp(findUser.Fullname, findUser.Email, codeRequest) + if err != nil { + return nil, err + } else { + msg := "Email is valid and OTP has been sent" + msgResponse = &msg + } + } + + return msgResponse, nil +} + +func (_i *usersService) SetupEmail(req request.UserEmailValidationRequest) (msgResponse *string, err error) { + _i.Log.Info().Interface("data", req).Msg("") + + var loginResponse *gocloak.JWT + loginResponse, err = _i.Keycloak.Login(*req.Username, *req.Password) + + if loginResponse == nil || err != nil { + return nil, fmt.Errorf("username / password incorrect") + } + + _i.Log.Info().Interface("findUser", "").Msg("") + findUser, err := _i.Repo.FindByUsername(*req.Username) + + _i.Log.Info().Interface("findUser", findUser).Msg("") + if findUser == nil || err != nil { + return nil, fmt.Errorf("username / password incorrect") + } + + isTrue := true + if findUser.Email == *req.OldEmail { + findUser.Email = *req.NewEmail + findUser.IsEmailUpdated = &isTrue + _i.Log.Info().Interface("Update", "").Msg("") + err = _i.Repo.Update(findUser.ID, findUser) + _i.Log.Info().Interface("Update", err).Msg("") + if err != nil { + return nil, err + } + + codeRequest, err := utilSvc.GenerateNumericCode(6) + if err != nil { + return nil, err + } + otpReq := entity.OneTimePasswords{ + Email: findUser.Email, + Identity: &findUser.Username, + OtpCode: codeRequest, + IsActive: true, + } + + err = _i.Repo.CreateOtp(&otpReq) + if err != nil { + return nil, err + } + + err = _i.SendLoginOtp(findUser.Fullname, findUser.Email, codeRequest) + if err != nil { + return nil, err + } else { + msg := "Email is valid and OTP has been sent" + msgResponse = &msg + } + } else { + return nil, fmt.Errorf("the old email is not same") + } + + return msgResponse, nil +} + +func ParseJWTToken(token string) (map[string]interface{}, error) { + // Pisahkan JWT menjadi 3 bagian: header, payload, dan signature + parts := strings.Split(token, ".") + if len(parts) != 3 { + return nil, fmt.Errorf("Invalid JWT token") + } + + // Decode bagian payload (index ke-1) + payloadData, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, fmt.Errorf("Failed to decode payload: %v", err) + } + + // Ubah payload menjadi map[string]interface{} + var payload map[string]interface{} + if err := json.Unmarshal(payloadData, &payload); err != nil { + return nil, fmt.Errorf("Failed to parse payload JSON: %v", err) + } + + return payload, nil +} + +func (_i *usersService) SendLoginOtp(name string, email string, otp string) error { + subject := "[HUMAS POLRI] Permintaan OTP" + htmlBody := fmt.Sprintf("

Hai %s !

Berikut kode OTP yang digunakan untuk Login.

", name) + htmlBody += fmt.Sprintf("

%s

", otp) + htmlBody += "

Kode diatas hanya berlaku selama 10 menit. Harap segera masukkan kode tersebut pada aplikasi HUMAS POLRI.

" + htmlBody += "

Demi menjaga kerahasiaan data kamu, mohon jangan membagikan kode OTP ke siapapun.

" + err := _i.Smtp.SendEmail(subject, email, name, htmlBody) + + return err +} + +func (_i *usersService) SendRegistrationOtp(name string, email string, otp string) error { + subject := "[HUMAS POLRI] Permintaan OTP" + htmlBody := fmt.Sprintf("

Hai %s !

Berikut kode OTP yang digunakan untuk Verifikasi Registrasi.

", name) + htmlBody += fmt.Sprintf("

%s

", otp) + htmlBody += "

Kode diatas hanya berlaku selama 10 menit. Harap segera masukkan kode tersebut pada aplikasi HUMAS POLRI.

" + htmlBody += "

Demi menjaga kerahasiaan data kamu, mohon jangan membagikan kode OTP ke siapapun.

" + err := _i.Smtp.SendEmail(subject, email, name, htmlBody) + + return err +} diff --git a/app/module/users/users.module.go b/app/module/users/users.module.go new file mode 100644 index 0000000..d395d09 --- /dev/null +++ b/app/module/users/users.module.go @@ -0,0 +1,65 @@ +package users + +import ( + "narasi-ahli-be/app/module/users/controller" + "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/app/module/users/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of UsersRouter +type UsersRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of Users module +var NewUsersModule = fx.Options( + // register repository of Users module + fx.Provide(repository.NewUsersRepository), + + // register service of Users module + fx.Provide(service.NewUsersService), + + // register controller of Users module + fx.Provide(controller.NewController), + + // register router of Users module + fx.Provide(NewUsersRouter), +) + +// init UsersRouter +func NewUsersRouter(fiber *fiber.App, controller *controller.Controller) *UsersRouter { + return &UsersRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of Users module +func (_i *UsersRouter) RegisterUsersRoutes() { + // define controllers + usersController := _i.Controller.Users + + // define routes + _i.App.Route("/users", func(router fiber.Router) { + router.Get("/", usersController.All) + router.Get("/detail/:id", usersController.Show) + router.Get("/username/:username", usersController.ShowByUsername) + router.Get("/info", usersController.ShowInfo) + router.Post("/", usersController.Save) + router.Put("/:id", usersController.Update) + router.Post("/login", usersController.Login) + router.Post("/pareto-login", usersController.ParetoLogin) + router.Delete("/:id", usersController.Delete) + router.Post("/save-password", usersController.SavePassword) + router.Post("/reset-password", usersController.ResetPassword) + router.Post("/forgot-password", usersController.ForgotPassword) + router.Post("/otp-request", usersController.OtpRequest) + router.Post("/otp-validation", usersController.OtpValidation) + router.Post("/email-validation", usersController.EmailValidation) + router.Post("/setup-email", usersController.SetupEmail) + }) +} diff --git a/app/module/work_history/controller/controller.go b/app/module/work_history/controller/controller.go new file mode 100644 index 0000000..c8df124 --- /dev/null +++ b/app/module/work_history/controller/controller.go @@ -0,0 +1,13 @@ +package controller + +import "narasi-ahli-be/app/module/work_history/service" + +type Controller struct { + WorkHistory WorkHistoryController +} + +func NewController(WorkHistoryService service.WorkHistoryService) *Controller { + return &Controller{ + WorkHistory: NewWorkHistoryController(WorkHistoryService), + } +} diff --git a/app/module/work_history/controller/work_history.controller.go b/app/module/work_history/controller/work_history.controller.go new file mode 100644 index 0000000..8d6397e --- /dev/null +++ b/app/module/work_history/controller/work_history.controller.go @@ -0,0 +1,208 @@ +package controller + +import ( + "narasi-ahli-be/app/module/work_history/request" + "narasi-ahli-be/app/module/work_history/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type workHistoryController struct { + workHistoryService service.WorkHistoryService +} + +type WorkHistoryController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewWorkHistoryController(workHistoryService service.WorkHistoryService) WorkHistoryController { + return &workHistoryController{ + workHistoryService: workHistoryService, + } +} + +// All Work History +// @Summary Get all Work History +// @Description API for getting all Work History for authenticated user +// @Tags Work History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.WorkHistoryQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /work-history [get] +func (_i *workHistoryController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + reqContext := request.WorkHistoryQueryRequestContext{ + JobTitle: c.Query("jobTitle"), + CompanyName: c.Query("companyName"), + IsCurrent: c.Query("isCurrent"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + workData, paging, err := _i.workHistoryService.All(authHeader, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Work history list successfully retrieved"}, + Data: workData, + Meta: paging, + }) +} + +// Show Work History +// @Summary Get one Work History +// @Description API for getting one Work History +// @Tags Work History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Work History ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /work-history/{id} [get] +func (_i *workHistoryController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + workData, err := _i.workHistoryService.Show(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Work history successfully retrieved"}, + Data: workData, + }) +} + +// Save Work History +// @Summary Create Work History +// @Description API for create Work History +// @Tags Work History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.WorkHistoryCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /work-history [post] +func (_i *workHistoryController) Save(c *fiber.Ctx) error { + req := new(request.WorkHistoryCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + dataResult, err := _i.workHistoryService.Save(authHeader, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Work history successfully created"}, + Data: dataResult, + }) +} + +// Update Work History +// @Summary Update Work History +// @Description API for update Work History +// @Tags Work History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Work History ID" +// @Param payload body request.WorkHistoryUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /work-history/{id} [put] +func (_i *workHistoryController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.WorkHistoryUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.workHistoryService.Update(authHeader, uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Work history successfully updated"}, + }) +} + +// Delete Work History +// @Summary Delete Work History +// @Description API for delete Work History +// @Tags Work History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param id path int true "Work History ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /work-history/{id} [delete] +func (_i *workHistoryController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + + err = _i.workHistoryService.Delete(authHeader, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Work history successfully deleted"}, + }) +} diff --git a/app/module/work_history/mapper/work_history.mapper.go b/app/module/work_history/mapper/work_history.mapper.go new file mode 100644 index 0000000..3335c44 --- /dev/null +++ b/app/module/work_history/mapper/work_history.mapper.go @@ -0,0 +1,59 @@ +package mapper + +import ( + "fmt" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/work_history/response" + "time" +) + +func WorkHistoryResponseMapper(workHistory *entity.WorkHistory) *response.WorkHistoryResponse { + result := &response.WorkHistoryResponse{ + ID: workHistory.ID, + UserID: workHistory.UserID, + JobTitle: workHistory.JobTitle, + CompanyName: workHistory.CompanyName, + StartDate: workHistory.StartDate, + EndDate: workHistory.EndDate, + IsCurrent: workHistory.EndDate == nil, + Duration: calculateDuration(workHistory.StartDate, workHistory.EndDate), + CreatedAt: workHistory.CreatedAt, + UpdatedAt: workHistory.UpdatedAt, + } + + if workHistory.User != nil { + result.User = &response.UserBasicInfo{ + ID: workHistory.User.ID, + Username: workHistory.User.Username, + Fullname: workHistory.User.Fullname, + Email: workHistory.User.Email, + } + } + + return result +} + +func calculateDuration(startDate time.Time, endDate *time.Time) string { + var end time.Time + if endDate == nil { + end = time.Now() + } else { + end = *endDate + } + + duration := end.Sub(startDate) + days := int(duration.Hours() / 24) + years := days / 365 + months := (days % 365) / 30 + + if years > 0 { + if months > 0 { + return fmt.Sprintf("%d tahun %d bulan", years, months) + } + return fmt.Sprintf("%d tahun", years) + } else if months > 0 { + return fmt.Sprintf("%d bulan", months) + } else { + return fmt.Sprintf("%d hari", days) + } +} diff --git a/app/module/work_history/repository/work_history.repository.go b/app/module/work_history/repository/work_history.repository.go new file mode 100644 index 0000000..370a9b7 --- /dev/null +++ b/app/module/work_history/repository/work_history.repository.go @@ -0,0 +1,88 @@ +package repository + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/work_history/request" + "narasi-ahli-be/utils/paginator" +) + +type workHistoryRepository struct { + DB *database.Database +} + +type WorkHistoryRepository interface { + GetAll(userId uint, req request.WorkHistoryQueryRequest) (workHistories []*entity.WorkHistory, paging paginator.Pagination, err error) + FindOneByUserAndId(userId uint, id uint) (workHistory *entity.WorkHistory, err error) + Create(workHistory *entity.WorkHistory) (result *entity.WorkHistory, err error) + Update(userId uint, id uint, workHistory *entity.WorkHistory) (err error) + Delete(userId uint, id uint) (err error) +} + +func NewWorkHistoryRepository(db *database.Database) WorkHistoryRepository { + return &workHistoryRepository{ + DB: db, + } +} + +func (_i *workHistoryRepository) GetAll(userId uint, req request.WorkHistoryQueryRequest) (workHistories []*entity.WorkHistory, paging paginator.Pagination, err error) { + query := _i.DB.DB.Where("user_id = ?", userId) + + // Apply filters + if req.JobTitle != nil { + query = query.Where("job_title ILIKE ?", "%"+*req.JobTitle+"%") + } + if req.CompanyName != nil { + query = query.Where("company_name ILIKE ?", "%"+*req.CompanyName+"%") + } + if req.IsCurrent != nil { + if *req.IsCurrent { + query = query.Where("end_date IS NULL") + } else { + query = query.Where("end_date IS NOT NULL") + } + } + + // Include user relationship + query = query.Preload("User") + + // Order by start date desc (most recent first) + query = query.Order("start_date DESC") + + // Apply pagination + var count int64 + query.Count(&count) + req.Pagination.Count = count + req.Pagination = paginator.Paging(req.Pagination) + + err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&workHistories).Error + paging = *req.Pagination + + return +} + +func (_i *workHistoryRepository) FindOneByUserAndId(userId uint, id uint) (workHistory *entity.WorkHistory, err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Preload("User").First(&workHistory).Error + return +} + +func (_i *workHistoryRepository) Create(workHistory *entity.WorkHistory) (result *entity.WorkHistory, err error) { + err = _i.DB.DB.Create(workHistory).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.DB.Preload("User").First(&result, workHistory.ID).Error + return +} + +func (_i *workHistoryRepository) Update(userId uint, id uint, workHistory *entity.WorkHistory) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Updates(workHistory).Error + return +} + +func (_i *workHistoryRepository) Delete(userId uint, id uint) (err error) { + err = _i.DB.DB.Where("user_id = ? AND id = ?", userId, id).Delete(&entity.WorkHistory{}).Error + return +} diff --git a/app/module/work_history/request/work_history.request.go b/app/module/work_history/request/work_history.request.go new file mode 100644 index 0000000..a3d36bf --- /dev/null +++ b/app/module/work_history/request/work_history.request.go @@ -0,0 +1,74 @@ +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "time" +) + +type WorkHistoryQueryRequest struct { + JobTitle *string `json:"jobTitle"` + CompanyName *string `json:"companyName"` + IsCurrent *bool `json:"isCurrent"` // filter for current job (EndDate is null) + Pagination *paginator.Pagination `json:"pagination"` +} + +type WorkHistoryCreateRequest struct { + JobTitle string `json:"jobTitle" validate:"required,min=2,max=255"` + CompanyName string `json:"companyName" validate:"required,min=2,max=255"` + StartDate time.Time `json:"startDate" validate:"required"` + EndDate *time.Time `json:"endDate"` +} + +func (req WorkHistoryCreateRequest) ToEntity() *entity.WorkHistory { + return &entity.WorkHistory{ + JobTitle: req.JobTitle, + CompanyName: req.CompanyName, + StartDate: req.StartDate, + EndDate: req.EndDate, + } +} + +type WorkHistoryUpdateRequest struct { + JobTitle string `json:"jobTitle" validate:"required,min=2,max=255"` + CompanyName string `json:"companyName" validate:"required,min=2,max=255"` + StartDate time.Time `json:"startDate" validate:"required"` + EndDate *time.Time `json:"endDate"` +} + +func (req WorkHistoryUpdateRequest) ToEntity() *entity.WorkHistory { + return &entity.WorkHistory{ + JobTitle: req.JobTitle, + CompanyName: req.CompanyName, + StartDate: req.StartDate, + EndDate: req.EndDate, + } +} + +type WorkHistoryQueryRequestContext struct { + JobTitle string `json:"jobTitle"` + CompanyName string `json:"companyName"` + IsCurrent string `json:"isCurrent"` +} + +func (req WorkHistoryQueryRequestContext) ToParamRequest() WorkHistoryQueryRequest { + var request WorkHistoryQueryRequest + + if jobTitle := req.JobTitle; jobTitle != "" { + request.JobTitle = &jobTitle + } + if companyName := req.CompanyName; companyName != "" { + request.CompanyName = &companyName + } + if isCurrent := req.IsCurrent; isCurrent != "" { + if isCurrent == "true" { + isCurrentBool := true + request.IsCurrent = &isCurrentBool + } else if isCurrent == "false" { + isCurrentBool := false + request.IsCurrent = &isCurrentBool + } + } + + return request +} diff --git a/app/module/work_history/response/work_history.response.go b/app/module/work_history/response/work_history.response.go new file mode 100644 index 0000000..e8be493 --- /dev/null +++ b/app/module/work_history/response/work_history.response.go @@ -0,0 +1,26 @@ +package response + +import ( + "time" +) + +type WorkHistoryResponse struct { + ID uint `json:"id"` + UserID uint `json:"userId"` + JobTitle string `json:"jobTitle"` + CompanyName string `json:"companyName"` + StartDate time.Time `json:"startDate"` + EndDate *time.Time `json:"endDate"` + IsCurrent bool `json:"isCurrent"` // true if EndDate is null + Duration string `json:"duration"` // calculated duration string + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + User *UserBasicInfo `json:"user,omitempty"` +} + +type UserBasicInfo struct { + ID uint `json:"id"` + Username string `json:"username"` + Fullname string `json:"fullname"` + Email string `json:"email"` +} diff --git a/app/module/work_history/service/work_history.service.go b/app/module/work_history/service/work_history.service.go new file mode 100644 index 0000000..1ec38c4 --- /dev/null +++ b/app/module/work_history/service/work_history.service.go @@ -0,0 +1,138 @@ +package service + +import ( + "errors" + usersRepository "narasi-ahli-be/app/module/users/repository" + "narasi-ahli-be/app/module/work_history/mapper" + "narasi-ahli-be/app/module/work_history/repository" + "narasi-ahli-be/app/module/work_history/request" + "narasi-ahli-be/app/module/work_history/response" + "narasi-ahli-be/utils/paginator" + utilSvc "narasi-ahli-be/utils/service" + + "github.com/rs/zerolog" +) + +type workHistoryService struct { + Repo repository.WorkHistoryRepository + UsersRepo usersRepository.UsersRepository + Log zerolog.Logger +} + +type WorkHistoryService interface { + All(authToken string, req request.WorkHistoryQueryRequest) (workHistories []*response.WorkHistoryResponse, paging paginator.Pagination, err error) + Show(authToken string, id uint) (workHistory *response.WorkHistoryResponse, err error) + Save(authToken string, req request.WorkHistoryCreateRequest) (workHistory *response.WorkHistoryResponse, err error) + Update(authToken string, id uint, req request.WorkHistoryUpdateRequest) (err error) + Delete(authToken string, id uint) error +} + +func NewWorkHistoryService(repo repository.WorkHistoryRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger) WorkHistoryService { + return &workHistoryService{ + Repo: repo, + UsersRepo: usersRepo, + Log: log, + } +} + +func (_i *workHistoryService) All(authToken string, req request.WorkHistoryQueryRequest) (workHistories []*response.WorkHistoryResponse, paging paginator.Pagination, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, paginator.Pagination{}, errors.New("unauthorized") + } + + results, paging, err := _i.Repo.GetAll(userInfo.ID, req) + if err != nil { + return + } + + for _, result := range results { + workHistories = append(workHistories, mapper.WorkHistoryResponseMapper(result)) + } + + return +} + +func (_i *workHistoryService) Show(authToken string, id uint) (workHistory *response.WorkHistoryResponse, err error) { + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("unauthorized") + } + + result, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return nil, err + } + + return mapper.WorkHistoryResponseMapper(result), nil +} + +func (_i *workHistoryService) Save(authToken string, req request.WorkHistoryCreateRequest) (workHistory *response.WorkHistoryResponse, err error) { + _i.Log.Info().Interface("data", req).Msg("Creating work history") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return nil, errors.New("unauthorized") + } + + // Validate business rules + if req.EndDate != nil && req.EndDate.Before(req.StartDate) { + return nil, errors.New("end date cannot be before start date") + } + + entity := req.ToEntity() + entity.UserID = userInfo.ID + + result, err := _i.Repo.Create(entity) + if err != nil { + return nil, err + } + + return mapper.WorkHistoryResponseMapper(result), nil +} + +func (_i *workHistoryService) Update(authToken string, id uint, req request.WorkHistoryUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Updating work history") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("work history not found") + } + + // Validate business rules + if req.EndDate != nil && req.EndDate.Before(req.StartDate) { + return errors.New("end date cannot be before start date") + } + + entity := req.ToEntity() + return _i.Repo.Update(userInfo.ID, id, entity) +} + +func (_i *workHistoryService) Delete(authToken string, id uint) error { + _i.Log.Info().Str("authToken", authToken).Uint("id", id).Msg("Deleting work history") + + userInfo := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) + if userInfo == nil { + return errors.New("unauthorized") + } + + // Check if record exists and belongs to user + existing, err := _i.Repo.FindOneByUserAndId(userInfo.ID, id) + if err != nil { + return err + } + if existing == nil { + return errors.New("work history not found") + } + + return _i.Repo.Delete(userInfo.ID, id) +} diff --git a/app/module/work_history/work_history.module.go b/app/module/work_history/work_history.module.go new file mode 100644 index 0000000..47dd879 --- /dev/null +++ b/app/module/work_history/work_history.module.go @@ -0,0 +1,54 @@ +package work_history + +import ( + "narasi-ahli-be/app/module/work_history/controller" + "narasi-ahli-be/app/module/work_history/repository" + "narasi-ahli-be/app/module/work_history/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +// struct of WorkHistoryRouter +type WorkHistoryRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of WorkHistory module +var NewWorkHistoryModule = fx.Options( + // register repository of WorkHistory module + fx.Provide(repository.NewWorkHistoryRepository), + + // register service of WorkHistory module + fx.Provide(service.NewWorkHistoryService), + + // register controller of WorkHistory module + fx.Provide(controller.NewController), + + // register router of WorkHistory module + fx.Provide(NewWorkHistoryRouter), +) + +// init WorkHistoryRouter +func NewWorkHistoryRouter(fiber *fiber.App, controller *controller.Controller) *WorkHistoryRouter { + return &WorkHistoryRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of WorkHistory module +func (_i *WorkHistoryRouter) RegisterWorkHistoryRoutes() { + // define controllers + workHistoryController := _i.Controller.WorkHistory + + // define routes + _i.App.Route("/work-history", func(router fiber.Router) { + router.Get("/", workHistoryController.All) + router.Get("/:id", workHistoryController.Show) + router.Post("/", workHistoryController.Save) + router.Put("/:id", workHistoryController.Update) + router.Delete("/:id", workHistoryController.Delete) + }) +} diff --git a/app/router/api.go b/app/router/api.go new file mode 100644 index 0000000..b2c4c4c --- /dev/null +++ b/app/router/api.go @@ -0,0 +1,164 @@ +package router + +import ( + "narasi-ahli-be/app/module/activity_logs" + "narasi-ahli-be/app/module/advertisement" + "narasi-ahli-be/app/module/article_approvals" + "narasi-ahli-be/app/module/article_categories" + "narasi-ahli-be/app/module/article_category_details" + "narasi-ahli-be/app/module/article_comments" + "narasi-ahli-be/app/module/article_files" + "narasi-ahli-be/app/module/articles" + "narasi-ahli-be/app/module/cities" + "narasi-ahli-be/app/module/custom_static_pages" + "narasi-ahli-be/app/module/districts" + "narasi-ahli-be/app/module/education_history" + "narasi-ahli-be/app/module/feedbacks" + "narasi-ahli-be/app/module/magazine_files" + "narasi-ahli-be/app/module/magazines" + "narasi-ahli-be/app/module/master_menus" + "narasi-ahli-be/app/module/master_modules" + "narasi-ahli-be/app/module/provinces" + "narasi-ahli-be/app/module/research_journals" + "narasi-ahli-be/app/module/subscription" + "narasi-ahli-be/app/module/user_levels" + "narasi-ahli-be/app/module/user_role_accesses" + "narasi-ahli-be/app/module/user_roles" + "narasi-ahli-be/app/module/users" + "narasi-ahli-be/app/module/work_history" + "narasi-ahli-be/config/config" + _ "narasi-ahli-be/docs/swagger" + + swagger "github.com/arsmn/fiber-swagger/v2" + "github.com/gofiber/fiber/v2" +) + +type Router struct { + App fiber.Router + Cfg *config.Config + + ActivityLogsRouter *activity_logs.ActivityLogsRouter + AdvertisementRouter *advertisement.AdvertisementRouter + ArticleCategoriesRouter *article_categories.ArticleCategoriesRouter + ArticleCategoryDetailsRouter *article_category_details.ArticleCategoryDetailsRouter + ArticleFilesRouter *article_files.ArticleFilesRouter + ArticleCommentsRouter *article_comments.ArticleCommentsRouter + ArticleApprovalsRouter *article_approvals.ArticleApprovalsRouter + ArticlesRouter *articles.ArticlesRouter + CitiesRouter *cities.CitiesRouter + CustomStaticPagesRouter *custom_static_pages.CustomStaticPagesRouter + DistrictsRouter *districts.DistrictsRouter + FeedbacksRouter *feedbacks.FeedbacksRouter + MagazineFilesRouter *magazine_files.MagazineFilesRouter + MagazinesRouter *magazines.MagazinesRouter + MasterMenusRouter *master_menus.MasterMenusRouter + MasterModulesRouter *master_modules.MasterModulesRouter + ProvincesRouter *provinces.ProvincesRouter + SubscriptionRouter *subscription.SubscriptionRouter + UserLevelsRouter *user_levels.UserLevelsRouter + UserRoleAccessesRouter *user_role_accesses.UserRoleAccessesRouter + UserRolesRouter *user_roles.UserRolesRouter + UsersRouter *users.UsersRouter + EducationHistoryRouter *education_history.EducationHistoryRouter + WorkHistoryRouter *work_history.WorkHistoryRouter + ResearchJournalsRouter *research_journals.ResearchJournalsRouter +} + +func NewRouter( + fiber *fiber.App, + cfg *config.Config, + + activityLogsRouter *activity_logs.ActivityLogsRouter, + advertisementRouter *advertisement.AdvertisementRouter, + articleCategoriesRouter *article_categories.ArticleCategoriesRouter, + articleCategoryDetailsRouter *article_category_details.ArticleCategoryDetailsRouter, + articleFilesRouter *article_files.ArticleFilesRouter, + articleCommentsRouter *article_comments.ArticleCommentsRouter, + articleApprovalsRouter *article_approvals.ArticleApprovalsRouter, + articlesRouter *articles.ArticlesRouter, + citiesRouter *cities.CitiesRouter, + customStaticPagesRouter *custom_static_pages.CustomStaticPagesRouter, + districtsRouter *districts.DistrictsRouter, + feedbacksRouter *feedbacks.FeedbacksRouter, + magazineFilesRouter *magazine_files.MagazineFilesRouter, + magazinesRouter *magazines.MagazinesRouter, + masterMenuRouter *master_menus.MasterMenusRouter, + masterModuleRouter *master_modules.MasterModulesRouter, + provincesRouter *provinces.ProvincesRouter, + subscriptionRouter *subscription.SubscriptionRouter, + userLevelsRouter *user_levels.UserLevelsRouter, + userRoleAccessesRouter *user_role_accesses.UserRoleAccessesRouter, + userRolesRouter *user_roles.UserRolesRouter, + usersRouter *users.UsersRouter, + educationHistoryRouter *education_history.EducationHistoryRouter, + workHistoryRouter *work_history.WorkHistoryRouter, + researchJournalsRouter *research_journals.ResearchJournalsRouter, +) *Router { + return &Router{ + App: fiber, + Cfg: cfg, + ActivityLogsRouter: activityLogsRouter, + AdvertisementRouter: advertisementRouter, + ArticleCategoriesRouter: articleCategoriesRouter, + ArticleCategoryDetailsRouter: articleCategoryDetailsRouter, + ArticleFilesRouter: articleFilesRouter, + ArticleCommentsRouter: articleCommentsRouter, + ArticleApprovalsRouter: articleApprovalsRouter, + ArticlesRouter: articlesRouter, + CitiesRouter: citiesRouter, + CustomStaticPagesRouter: customStaticPagesRouter, + DistrictsRouter: districtsRouter, + FeedbacksRouter: feedbacksRouter, + MagazineFilesRouter: magazineFilesRouter, + MagazinesRouter: magazinesRouter, + MasterMenusRouter: masterMenuRouter, + MasterModulesRouter: masterModuleRouter, + ProvincesRouter: provincesRouter, + SubscriptionRouter: subscriptionRouter, + UserLevelsRouter: userLevelsRouter, + UserRoleAccessesRouter: userRoleAccessesRouter, + UserRolesRouter: userRolesRouter, + UsersRouter: usersRouter, + EducationHistoryRouter: educationHistoryRouter, + WorkHistoryRouter: workHistoryRouter, + ResearchJournalsRouter: researchJournalsRouter, + } +} + +// Register routes +func (r *Router) Register() { + // Test Routes + r.App.Get("/ping", func(c *fiber.Ctx) error { + return c.SendString("Pong! 👋") + }) + + //Swagger Documentation + r.App.Get("/swagger/*", swagger.HandlerDefault) + + // Register routes of modules + r.ActivityLogsRouter.RegisterActivityLogsRoutes() + r.AdvertisementRouter.RegisterAdvertisementRoutes() + r.ArticleCategoriesRouter.RegisterArticleCategoriesRoutes() + r.ArticleCategoryDetailsRouter.RegisterArticleCategoryDetailsRoutes() + r.ArticleFilesRouter.RegisterArticleFilesRoutes() + r.ArticleApprovalsRouter.RegisterArticleApprovalsRoutes() + r.ArticlesRouter.RegisterArticlesRoutes() + r.ArticleCommentsRouter.RegisterArticleCommentsRoutes() + r.CitiesRouter.RegisterCitiesRoutes() + r.CustomStaticPagesRouter.RegisterCustomStaticPagesRoutes() + r.DistrictsRouter.RegisterDistrictsRoutes() + r.FeedbacksRouter.RegisterFeedbacksRoutes() + r.MagazinesRouter.RegisterMagazinesRoutes() + r.MagazineFilesRouter.RegisterMagazineFilesRoutes() + r.MasterMenusRouter.RegisterMasterMenusRoutes() + r.MasterModulesRouter.RegisterMasterModulesRoutes() + r.ProvincesRouter.RegisterProvincesRoutes() + r.SubscriptionRouter.RegisterSubscriptionRoutes() + r.UserLevelsRouter.RegisterUserLevelsRoutes() + r.UserRoleAccessesRouter.RegisterUserRoleAccessesRoutes() + r.UsersRouter.RegisterUsersRoutes() + r.UserRolesRouter.RegisterUserRolesRoutes() + r.EducationHistoryRouter.RegisterEducationHistoryRoutes() + r.WorkHistoryRouter.RegisterWorkHistoryRoutes() + r.ResearchJournalsRouter.RegisterResearchJournalsRoutes() +} diff --git a/config/config/index.config.go b/config/config/index.config.go new file mode 100644 index 0000000..5f2d2e4 --- /dev/null +++ b/config/config/index.config.go @@ -0,0 +1,175 @@ +package config + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/compress" + "github.com/pelletier/go-toml/v2" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "os" + "path/filepath" + "runtime" + "strings" + "time" +) + +type app = struct { + Name string `toml:"name"` + Host string `toml:"host"` + Domain string `toml:"domain"` + Port string `toml:"port"` + ExternalPort string `toml:"external-port"` + PrintRoutes bool `toml:"print-routes"` + Prefork bool `toml:"prefork"` + Production bool `toml:"production"` + IdleTimeout time.Duration `toml:"idle-timeout"` + BodyLimit int `toml:"body-limit"` +} + +// db struct config +type db = struct { + Postgres struct { + DSN string `toml:"dsn"` + LogMode string `toml:"log-mode"` + Migrate bool `toml:"migrate"` + Seed bool `toml:"seed"` + } +} + +// log struct config +type logger = struct { + TimeFormat string `toml:"time-format"` + Level zerolog.Level `toml:"level"` + Prettier bool `toml:"prettier"` +} + +// middleware +type middleware = struct { + Compress struct { + Enable bool + Level compress.Level + } + + Recover struct { + Enable bool + } + + Monitor struct { + Enable bool + Path string + } + + Pprof struct { + Enable bool + } + + Cors struct { + Enable bool + } + + Limiter struct { + Enable bool + Max int + Expiration time.Duration `toml:"expiration_seconds"` + } + + Csrf struct { + Enable bool + CookieName string `toml:"cookie-name"` + CookieSameSite string `toml:"cookie-same-site"` + CookieSecure bool `toml:"cookie-secure"` + CookieSessionOnly bool `toml:"cookie-session-only"` + CookieHttpOnly bool `toml:"cookie-http-only"` + } + + AuditTrails struct { + Enable bool + Retention int + } +} + +// minio struct config +type objectStorage = struct { + MinioStorage struct { + Endpoint string `toml:"endpoint"` + AccessKeyID string `toml:"access-key-id"` + SecretAccessKey string `toml:"secret-access-key"` + UseSSL bool `toml:"use-ssl"` + BucketName string `toml:"bucket-name"` + Location string `toml:"location"` + } +} + +type keycloak = struct { + Endpoint string `toml:"endpoint"` + Realm string `toml:"realm"` + ClientId string `toml:"client-id"` + ClientSecret string `toml:"client-secret"` + AdminUsername string `toml:"admin-username"` + AdminPassword string `toml:"admin-password"` +} + +type smtp = struct { + Host string `toml:"host"` + Port int `toml:"port"` + Username string `toml:"username"` + Password string `toml:"password"` + FromAddress string `toml:"from-address"` + FromName string `toml:"from-name"` +} + +type Config struct { + App app + DB db + Logger logger + Middleware middleware + ObjectStorage objectStorage + Keycloak keycloak + Smtp smtp +} + +// NewConfig : initialize config +func NewConfig() *Config { + config, err := ParseConfig("config") + if err != nil && !fiber.IsChild() { + // panic if config is not found + log.Panic().Err(err).Msg("config not found") + } + + return config +} + +// ParseConfig : func to parse config +func ParseConfig(name string, debug ...bool) (*Config, error) { + var ( + contents *Config + file []byte + err error + ) + + if len(debug) > 0 { + file, err = os.ReadFile(name) + } else { + _, b, _, _ := runtime.Caller(0) + // get base path + path := filepath.Dir(filepath.Dir(filepath.Dir(b))) + file, err = os.ReadFile(filepath.Join(path, "./config/toml/", name+".toml")) + } + + if err != nil { + return &Config{}, err + } + + err = toml.Unmarshal(file, &contents) + + return contents, err +} + +// ParseAddress : func to parse address +func ParseAddress(raw string) (host, port string) { + if i := strings.LastIndex(raw, ":"); i > 0 { + return raw[:i], raw[i+1:] + } + + return raw, "" +} diff --git a/config/config/keycloak.config.go b/config/config/keycloak.config.go new file mode 100644 index 0000000..f2d4e97 --- /dev/null +++ b/config/config/keycloak.config.go @@ -0,0 +1,187 @@ +package config + +import ( + "context" + "errors" + "github.com/Nerzal/gocloak/v13" +) + +// MinioSetup struct +type KeycloakConfig struct { + Cfg *Config +} + +func NewKeycloakConfig(cfg *Config) *KeycloakConfig { + keycloakSetup := &KeycloakConfig{ + Cfg: cfg, + } + + return keycloakSetup +} + +func (_keycloak *KeycloakConfig) Login(username string, password string) (*gocloak.JWT, error) { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + loginResponse, err := client.Login( + ctx, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + username, + password, + ) + if err != nil { + return nil, errors.New("Invalid User Credentials") + } + + return loginResponse, nil +} + +func (_keycloak *KeycloakConfig) RefreshToken(refreshToken string) (*gocloak.JWT, error) { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + loginResponse, err := client.RefreshToken( + ctx, + refreshToken, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + ) + if err != nil { + return nil, errors.New("Invalid User Credentials") + } + + return loginResponse, nil +} + +func (_keycloak *KeycloakConfig) CreateUser(fullname string, email string, username string, password string) (string, error) { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + token, err := client.Login( + ctx, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + _keycloak.Cfg.Keycloak.AdminUsername, + _keycloak.Cfg.Keycloak.AdminPassword, + ) + if err != nil { + panic("Something wrong with the credentials or url") + } + + var group []string + group = append(group, "medols") + user := gocloak.User{ + FirstName: gocloak.StringP(fullname), + LastName: gocloak.StringP(""), + Email: gocloak.StringP(email), + Enabled: gocloak.BoolP(true), + Username: gocloak.StringP(username), + Groups: &group, + } + + keycloakId, err := client.CreateUser(ctx, token.AccessToken, _keycloak.Cfg.Keycloak.Realm, user) + if err != nil { + panic("Oh no!, failed to create user :(") + } + + err = _keycloak.SetPassword(token.AccessToken, keycloakId, password) + if err != nil { + return "", err + } + + return keycloakId, nil +} + +func (_keycloak *KeycloakConfig) UpdateUser(keycloakId *string, fullname string, email string) error { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + token, err := client.Login( + ctx, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + _keycloak.Cfg.Keycloak.AdminUsername, + _keycloak.Cfg.Keycloak.AdminPassword, + ) + if err != nil { + panic("Something wrong with the credentials or url") + } + + var group []string + group = append(group, "medols") + user := gocloak.User{ + ID: keycloakId, + FirstName: gocloak.StringP(fullname), + LastName: gocloak.StringP(""), + Email: gocloak.StringP(email), + Groups: &group, + } + + err = client.UpdateUser(ctx, token.AccessToken, _keycloak.Cfg.Keycloak.Realm, user) + if err != nil { + panic(err) + } + + return err +} + +func (_keycloak *KeycloakConfig) SetPassword(token string, keycloakId string, password string) error { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + + err := client.SetPassword(ctx, token, keycloakId, _keycloak.Cfg.Keycloak.Realm, password, false) + if err != nil { + panic("Oh no!, failed to set password :(") + } + + return nil +} + +func (_keycloak *KeycloakConfig) SetPasswordWithoutToken(keycloakId string, password string) error { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + + token, err := client.Login( + ctx, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + _keycloak.Cfg.Keycloak.AdminUsername, + _keycloak.Cfg.Keycloak.AdminPassword, + ) + if err != nil { + panic("Something wrong with the credentials or url") + } + + err = client.SetPassword(ctx, token.AccessToken, keycloakId, _keycloak.Cfg.Keycloak.Realm, password, false) + if err != nil { + panic("Oh no!, failed to set password :(") + } + + return nil +} + +func (_keycloak *KeycloakConfig) GetUserSessions() ([]*gocloak.UserSessionRepresentation, error) { + ctx := context.Background() + client := gocloak.NewClient(_keycloak.Cfg.Keycloak.Endpoint) + + token, err := client.Login( + ctx, + _keycloak.Cfg.Keycloak.ClientId, + _keycloak.Cfg.Keycloak.ClientSecret, + _keycloak.Cfg.Keycloak.Realm, + _keycloak.Cfg.Keycloak.AdminUsername, + _keycloak.Cfg.Keycloak.AdminPassword, + ) + if err != nil { + panic("Something wrong with the credentials or url") + } + + sessionData, err := client.GetClientUserSessions(ctx, token.AccessToken, _keycloak.Cfg.Keycloak.Realm, _keycloak.Cfg.Keycloak.ClientId) + if err != nil { + panic("Oh no!, failed to set password :(") + } + + return sessionData, nil +} diff --git a/config/config/minio.config.go b/config/config/minio.config.go new file mode 100644 index 0000000..883b257 --- /dev/null +++ b/config/config/minio.config.go @@ -0,0 +1,55 @@ +package config + +import ( + "context" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "log" +) + +// MinioSetup struct +type MinioStorage struct { + Cfg *Config +} + +func NewMinio(cfg *Config) *MinioStorage { + minioSetup := &MinioStorage{ + Cfg: cfg, + } + + return minioSetup +} + +func (_minio *MinioStorage) ConnectMinio() (*minio.Client, error) { + ctx := context.Background() + endpoint := _minio.Cfg.ObjectStorage.MinioStorage.Endpoint + accessKeyID := _minio.Cfg.ObjectStorage.MinioStorage.AccessKeyID + secretAccessKey := _minio.Cfg.ObjectStorage.MinioStorage.SecretAccessKey + useSSL := _minio.Cfg.ObjectStorage.MinioStorage.UseSSL + + bucketName := _minio.Cfg.ObjectStorage.MinioStorage.BucketName + location := _minio.Cfg.ObjectStorage.MinioStorage.Location + + // Initialize minio client object. + minioClient, errInit := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), + Secure: useSSL, + }) + if errInit != nil { + log.Fatalln(errInit) + } + + err := minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location}) + if err != nil { + // Check to see if we already own this bucket (which happens if you run this twice) + exists, errBucketExists := minioClient.BucketExists(ctx, bucketName) + if errBucketExists == nil && exists { + log.Printf("We already own %s\n", bucketName) + } else { + log.Fatalln(err) + } + } else { + log.Printf("Successfully created %s\n", bucketName) + } + return minioClient, errInit +} diff --git a/config/config/smtp.config.go b/config/config/smtp.config.go new file mode 100644 index 0000000..a376074 --- /dev/null +++ b/config/config/smtp.config.go @@ -0,0 +1,64 @@ +package config + +import ( + "fmt" + "github.com/wneessen/go-mail" + ht "html/template" +) + +// StmpConfig struct +type SmtpConfig struct { + Cfg *Config +} + +func NewSmtpConfig(cfg *Config) *SmtpConfig { + smtpSetup := &SmtpConfig{ + Cfg: cfg, + } + + return smtpSetup +} + +func (_smtp *SmtpConfig) SendEmail(subject string, toAddress string, toName string, htmlBody string) (err error) { + println(_smtp.Cfg.Smtp.Host) + println(_smtp.Cfg.Smtp.Port) + println(_smtp.Cfg.Smtp.Username) + println(_smtp.Cfg.Smtp.Password) + println(subject) + println(toAddress) + println(toName) + println(htmlBody) + + client, err := mail.NewClient(_smtp.Cfg.Smtp.Host, mail.WithPort(_smtp.Cfg.Smtp.Port), mail.WithSSL(), + mail.WithSMTPAuth(mail.SMTPAuthPlain), mail.WithUsername(_smtp.Cfg.Smtp.Username), mail.WithPassword(_smtp.Cfg.Smtp.Password)) + if err != nil { + return fmt.Errorf("failed to create mail client: %s\n", err) + } + + message := mail.NewMsg() + + if err := message.EnvelopeFrom(_smtp.Cfg.Smtp.FromAddress); err != nil { + return fmt.Errorf("failed to set ENVELOPE FROM address: %s", err) + } + if err := message.FromFormat(_smtp.Cfg.Smtp.FromName, _smtp.Cfg.Smtp.FromAddress); err != nil { + return fmt.Errorf("failed to set formatted FROM address: %s", err) + } + if err := message.AddToFormat(toName, toAddress); err != nil { + return fmt.Errorf("failed to set formatted TO address: %s", err) + } + + message.SetMessageID() + message.SetDate() + message.Subject(subject) + + htmlTpl, err := ht.New("htmltpl").Parse(htmlBody) + if err := message.SetBodyHTMLTemplate(htmlTpl, toAddress); err != nil { + return fmt.Errorf("failed to add HTML template to mail body: %s", err) + } + + if err = client.DialAndSend(message); err != nil { + return fmt.Errorf("failed to send mail: %s\n", err) + } + + return nil +} diff --git a/config/logger/index.logger.go b/config/logger/index.logger.go new file mode 100644 index 0000000..299523f --- /dev/null +++ b/config/logger/index.logger.go @@ -0,0 +1,46 @@ +package logger + +import ( + "narasi-ahli-be/config/config" + "os" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +// NewLogger : initialize logger +func NewLogger(cfg *config.Config) zerolog.Logger { + zerolog.TimeFieldFormat = cfg.Logger.TimeFormat + + if cfg.Logger.Prettier { + //log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}) + + logFile, _ := os.OpenFile( + "debug.log", + os.O_APPEND|os.O_CREATE|os.O_WRONLY, + 0664, + ) + + multi := zerolog.MultiLevelWriter(os.Stdout, logFile) + log.Logger = zerolog.New(multi).With().Timestamp().Logger() + } + + zerolog.SetGlobalLevel(cfg.Logger.Level) + + return log.Hook(PreforkHook{}) +} + +// PreforkHook : prefer hook for zerologger +type PreforkHook struct{} + +func (h PreforkHook) Run(e *zerolog.Event, level zerolog.Level, msg string) { + //if fiber.IsChild() { + // e.Discard() + //} +} + +type StringerFunc func() string + +func (f StringerFunc) String() string { + return f() +} diff --git a/config/toml/config.toml b/config/toml/config.toml new file mode 100644 index 0000000..47d85fb --- /dev/null +++ b/config/toml/config.toml @@ -0,0 +1,82 @@ +# Configuration vars for cmd/app +[app] +name = "Fiber starter" +host = "http://38.47.180.165" +port = ":8800" +domain = "https://dev.mikulnews.com/api" +external-port = ":8802" +idle-timeout = 5 # As seconds +print-routes = false +prefork = true +production = false +body-limit = 1048576000 # "100 * 1024 * 1024" + +[db.postgres] +dsn = "postgresql://medols_user:MedolsDB@2025@38.47.180.165:5432/medols_db" # ://:@:/ +log-mode = "NONE" +migrate = true +seed = true + +[logger] +log-dir = "debug.log" +time-format = "" # https://pkg.go.dev/time#pkg-constants, https://github.com/rs/zerolog/blob/master/api.go#L10 +level = 0 # panic -> 5, fatal -> 4, error -> 3, warn -> 2, info -> 1, debug -> 0, trace -> -1 +prettier = true + +[objectstorage.miniostorage] +endpoint = "38.47.180.165:9009" +access-key-id = "lBtjqWidHz1ktBbduwGy" +secret-access-key = "nsedJIa2FI7SqsEVcSFqJrlP4JuFRWGLauNpzD0i" +use-ssl = false +bucket-name = "mikulnews" +location = "us-east-1" + +[middleware.compress] +enable = true +level = 1 + +[middleware.recover] +enable = true + +[middleware.monitor] +enable = true +path = "/monitor" + +[middleware.pprof] +enable = true + +[middleware.cors] +enable = true + +[middleware.limiter] +enable = true +max = 20 +expiration_seconds = 60 + +[middleware.csrf] +enable = false +cookie-name = "csrf_" +cookie-same-site = "Lax" +cookie-secure = false +cookie-session-only = true +cookie-http-only = true + +[middleware.audittrails] +enable = true +retention = 30 + +[keycloak] +endpoint = "http://38.47.180.165:8008" +realm = "medols" +client-id = "medols-app" +client-secret = "iyonEpZbAUs20quwaNFLMwRX7MUgPRlS" +admin-username = "developer-admin" +admin-password = "P@ssw0rd.1" + +[smtp] +host = "mail.polri.go.id" +port = 465 +username = "webhumas.divhumas@polri.go.id" +password = "8vm1nxxTsaB6" +from-address = "webhumas.divhumas@polri.go.id" +from-name = "APLIKASI WEB HUMAS DIVHUMAS POLRI" \ No newline at end of file diff --git a/config/webserver/webserver.config.go b/config/webserver/webserver.config.go new file mode 100644 index 0000000..c8f53c9 --- /dev/null +++ b/config/webserver/webserver.config.go @@ -0,0 +1,185 @@ +package webserver + +import ( + "context" + "flag" + "fmt" + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/database/seeds" + md "narasi-ahli-be/app/middleware" + articlesService "narasi-ahli-be/app/module/articles/service" + "narasi-ahli-be/app/router" + "narasi-ahli-be/config/config" + "narasi-ahli-be/utils/response" + "os" + "runtime" + "strings" + "time" + + "github.com/go-co-op/gocron" + "github.com/gofiber/fiber/v2" + futils "github.com/gofiber/fiber/v2/utils" + "github.com/rs/zerolog" + "go.uber.org/fx" +) + +// NewFiber : initialize the webserver +func NewFiber(cfg *config.Config) *fiber.App { + // setup + app := fiber.New(fiber.Config{ + ServerHeader: cfg.App.Name, + AppName: cfg.App.Name, + Prefork: cfg.App.Prefork, + ErrorHandler: response.ErrorHandler, + IdleTimeout: cfg.App.IdleTimeout * time.Second, + EnablePrintRoutes: cfg.App.PrintRoutes, + BodyLimit: cfg.App.BodyLimit, + DisableStartupMessage: true, + ReadBufferSize: 8192, + }) + + // pass production config to check it + response.IsProduction = cfg.App.Production + + return app +} + +// Start : function to start webserver +func Start(lifecycle fx.Lifecycle, cfg *config.Config, fiber *fiber.App, router *router.Router, middlewares *md.Middleware, db *database.Database, log zerolog.Logger) { + lifecycle.Append( + fx.Hook{ + OnStart: func(ctx context.Context) error { + + // Connect database + db.ConnectDatabase() + + // Register middlewares + middlewares.Register(db) + + // Register routes + router.Register() + + // Custom Startup Messages + host, port := config.ParseAddress(cfg.App.Port) + if host == "" { + if fiber.Config().Network == "tcp6" { + host = "[::1]" + } else { + host = "0.0.0.0" + } + } + + // ASCII Art + ascii, err := os.ReadFile("./storage/ascii_art.txt") + if err != nil { + log.Debug().Err(err).Msg("An unknown error occurred when to print ASCII art!") + } + + for _, line := range strings.Split(futils.UnsafeString(ascii), "\n") { + log.Info().Msg(line) + } + + // Information message + log.Info().Msg(fiber.Config().AppName + " is running at the moment!") + + // Debug informations + if !cfg.App.Production { + prefork := "Enabled" + procs := runtime.GOMAXPROCS(0) + if !cfg.App.Prefork { + procs = 1 + prefork = "Disabled" + } + + log.Debug().Msgf("Version: %s", "-") + log.Debug().Msgf("Host: %s", host) + log.Debug().Msgf("Port: %s", port) + log.Debug().Msgf("Prefork: %s", prefork) + log.Debug().Msgf("Handlers: %d", fiber.HandlersCount()) + log.Debug().Msgf("Processes: %d", procs) + log.Debug().Msgf("PID: %d", os.Getpid()) + } + + // Listen the app (with TLS Support) + //if cfg.App.TLS.Enable { + // log.Debug().Msg("TLS support was enabled.") + // + // if err := fiber.ListenTLS(cfg.App.Port, cfg.App.TLS.CertFile, cfg.App.TLS.KeyFile); err != nil { + // log.Error().Err(err).Msg("An unknown error occurred when to run server!") + // } + //} + + go func() { + if err := fiber.Listen(cfg.App.Port); err != nil { + log.Error().Err(err).Msg("An unknown error occurred when to run server!") + } + }() + + migrateFlag := flag.Bool("migrate", db.Cfg.DB.Postgres.Migrate, "migrate the database") + seedFlag := flag.Bool("seed", db.Cfg.DB.Postgres.Seed, "seed the database") + flag.Parse() + + // read flag -migrate to migrate the database + if *migrateFlag { + db.MigrateModels() + } + // read flag -seed to seed the database + if *seedFlag { + + // init seed models + masterStatusSeeder := seeds.MasterStatusesSeeder{} + masterApprovalStatusSeeder := seeds.MasterApprovalStatusesSeeder{} + activityLogsSeeder := seeds.ActivityLogsSeeder{} + allSeeders := []database.Seeder{masterStatusSeeder, masterApprovalStatusSeeder, activityLogsSeeder} + + db.SeedModels(allSeeders) + } + + return nil + }, + OnStop: func(ctx context.Context) error { + log.Info().Msg("Shutting down the app...") + if err := fiber.Shutdown(); err != nil { + log.Panic().Err(err).Msg("") + } + + log.Info().Msg("Running cleanup tasks...") + log.Info().Msg("1- Shutdown the database") + db.ShutdownDatabase() + log.Info().Msgf("%s was successful shutdown.", cfg.App.Name) + log.Info().Msg("\u001b[96msee you again👋\u001b[0m") + + return nil + }, + }, + ) +} + +func RunScheduling(lifecycle fx.Lifecycle, articleService articlesService.ArticlesService) *gocron.Scheduler { + scheduler := gocron.NewScheduler(time.UTC) + + // Tambahkan tugas yang berjalan setiap 10 detik + // scheduler.Every(1).Minutes().SingletonMode().Do(func() { + // fmt.Println("Run Scheduler", time.Now()) + // err := articleService.ExecuteScheduling() + // if err != nil { + // fmt.Println("Scheduler Got An Error", time.Now()) + // } + // }) + + // Menambahkan lifecycle hooks di Fx + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + fmt.Println("Memulai scheduler...") + scheduler.StartAsync() // Mulai scheduler saat aplikasi dimulai + return nil + }, + OnStop: func(ctx context.Context) error { + fmt.Println("Menghentikan scheduler...") + scheduler.Stop() // Hentikan scheduler saat aplikasi dihentikan + return nil + }, + }) + + return scheduler +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d6c1eee --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3.8" + +services: + narasi-ahli-be: + image: registry.gitlab.com/hanifsalafi/narasi-ahli-be:dev + build: + context: . + dockerfile: Dockerfile + volumes: + - ./data/narasi-ahli-be/logs:/app + ports: + - "8800:8800" \ No newline at end of file diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go new file mode 100644 index 0000000..a22aed6 --- /dev/null +++ b/docs/swagger/docs.go @@ -0,0 +1,12692 @@ +// Package swagger Code generated by swaggo/swag. DO NOT EDIT +package swagger + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/activity-logs": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Get all ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "activityTypeId", + "in": "query" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "string", + "name": "url", + "in": "query" + }, + { + "type": "integer", + "name": "userId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Create ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ActivityLogsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/activity-logs/detail/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Get one ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ActivityLogs ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/activity-logs/statistics": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for get activity stats ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Get activity stats ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/activity-logs/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "update ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ActivityLogsUpdateRequest" + } + }, + { + "type": "integer", + "description": "ActivityLogs ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "delete ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ActivityLogs ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Get all Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "string", + "name": "placement", + "in": "query" + }, + { + "type": "string", + "name": "redirectLink", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Create Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AdvertisementCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/publish/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Update Publish Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Update Publish Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Advertisement Publish Status", + "name": "isPublish", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/upload/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Upload File Advertisement", + "produces": [ + "application/json" + ], + "tags": [ + "Advertisement" + ], + "summary": "Upload Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload file", + "name": "file", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Viewer Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Viewer Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Content File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Get one Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "update Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AdvertisementUpdateRequest" + } + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "delete Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-approvals": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "Get all ArticleApprovals", + "parameters": [ + { + "type": "integer", + "name": "approvalAtLevel", + "in": "query" + }, + { + "type": "integer", + "name": "approvalBy", + "in": "query" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "string", + "name": "message", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "Create ArticleApprovals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleApprovalsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-approvals/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "Get one ArticleApprovals", + "parameters": [ + { + "type": "integer", + "description": "ArticleApprovals ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "update ArticleApprovals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleApprovalsUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleApprovals ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "delete ArticleApprovals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleApprovals ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get all ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "name": "UserLevelId", + "in": "query" + }, + { + "type": "integer", + "name": "UserLevelNumber", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "integer", + "name": "parentId", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Create ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCategoriesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/old/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get one ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleCategories Old ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/slug/{slug}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get one ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "ArticleCategories Slug", + "name": "slug", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/thumbnail/viewer/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for View Thumbnail of ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Viewer ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/thumbnail/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Upload ArticleCategories Thumbnail", + "produces": [ + "application/json" + ], + "tags": [ + "Article Categories" + ], + "summary": "Upload ArticleCategories Thumbnail", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload thumbnail", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get one ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "update ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCategoriesUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "delete ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-category-details": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "Get all ArticleCategoryDetails", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "Create ArticleCategoryDetails", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/article-category-details/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "Get one ArticleCategoryDetails", + "parameters": [ + { + "type": "integer", + "description": "ArticleCategoryDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "update ArticleCategoryDetails", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategoryDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "delete ArticleCategoryDetails", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategoryDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/article-comments": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Get all ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "integer", + "name": "commentFrom", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublic", + "in": "query" + }, + { + "type": "string", + "name": "message", + "in": "query" + }, + { + "type": "integer", + "name": "parentId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Create ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCommentsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-comments/approval": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Approval ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Approval ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCommentsApprovalRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-comments/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Get one ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleComments ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "update ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCommentsUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleComments ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "delete ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleComments ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Get all ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "string", + "name": "fileName", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/upload-status/{uploadId}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for GetUploadStatus ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "GetUploadStatus ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Upload ID of ArticleFiles", + "name": "uploadId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Viewer ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Viewer ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Article File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/{articleId}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleFiles", + "produces": [ + "application/json" + ], + "tags": [ + "Article Files" + ], + "summary": "Upload ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Article ID", + "name": "articleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Get one ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Update ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleFilesUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Delete ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Articles", + "tags": [ + "Articles" + ], + "summary": "Get all Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header", + "required": true + }, + { + "type": "string", + "name": "category", + "in": "query" + }, + { + "type": "integer", + "name": "categoryId", + "in": "query" + }, + { + "type": "integer", + "name": "createdById", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isBanner", + "in": "query" + }, + { + "type": "boolean", + "name": "isDraft", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "tags", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "typeId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Articles", + "tags": [ + "Articles" + ], + "summary": "Create Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticlesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/banner/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Update Banner Articles", + "tags": [ + "Articles" + ], + "summary": "Update Banner Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Articles Banner Status", + "name": "isBanner", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/old-id/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Articles", + "tags": [ + "Articles" + ], + "summary": "Get one Articles", + "parameters": [ + { + "type": "integer", + "description": "Articles Old ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/publish-scheduling": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Publish Schedule of Article", + "tags": [ + "Articles" + ], + "summary": "PublishScheduling Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "article id", + "name": "id", + "in": "query" + }, + { + "type": "string", + "description": "publish date", + "name": "date", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/statistic/monthly": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticleMonthlyStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticleMonthlyStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "year", + "name": "year", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/statistic/summary": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Summary Stats of Article", + "tags": [ + "Articles" + ], + "summary": "SummaryStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/statistic/user-levels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticlePerUserLevelStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticlePerUserLevelStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "start date", + "name": "startDate", + "in": "query" + }, + { + "type": "string", + "description": "start date", + "name": "endDate", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/thumbnail/viewer/{thumbnailName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for View Thumbnail of Article", + "tags": [ + "Articles" + ], + "summary": "Viewer Articles Thumbnail", + "parameters": [ + { + "type": "string", + "description": "Articles Thumbnail Name", + "name": "thumbnailName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/thumbnail/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Save Thumbnail of Articles", + "produces": [ + "application/json" + ], + "tags": [ + "Articles" + ], + "summary": "Save Thumbnail Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload thumbnail", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Articles", + "tags": [ + "Articles" + ], + "summary": "Get one Articles", + "parameters": [ + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Articles", + "tags": [ + "Articles" + ], + "summary": "Update Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticlesUpdateRequest" + } + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Articles", + "tags": [ + "Articles" + ], + "summary": "Delete Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/cities": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Cities", + "tags": [ + "Untags" + ], + "summary": "Get all Cities", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Cities", + "tags": [ + "Untags" + ], + "summary": "Create Cities", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CitiesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/cities/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Cities", + "tags": [ + "Untags" + ], + "summary": "Get one Cities", + "parameters": [ + { + "type": "integer", + "description": "Cities ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Cities", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Untags" + ], + "summary": "Update Cities", + "parameters": [ + { + "type": "integer", + "description": "Cities ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CitiesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Cities", + "tags": [ + "Untags" + ], + "summary": "Delete Cities", + "parameters": [ + { + "type": "integer", + "description": "Cities ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/custom-static-pages": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Get all CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "string", + "name": "htmlBody", + "in": "query" + }, + { + "type": "string", + "name": "slug", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Create CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CustomStaticPagesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/custom-static-pages/slug/{slug}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Get one CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "CustomStaticPages Slug", + "name": "slug", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/custom-static-pages/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Get one CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "CustomStaticPages ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "update CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CustomStaticPagesUpdateRequest" + } + }, + { + "type": "integer", + "description": "CustomStaticPages ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "delete CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "CustomStaticPages ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/districts": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Districts", + "tags": [ + "Untags" + ], + "summary": "Get all Districts", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Districts", + "tags": [ + "Untags" + ], + "summary": "Create Districts", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/districts/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Districts", + "tags": [ + "Untags" + ], + "summary": "Get one Districts", + "parameters": [ + { + "type": "integer", + "description": "Districts ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Districts", + "tags": [ + "Untags" + ], + "summary": "Update Districts", + "parameters": [ + { + "type": "integer", + "description": "Districts ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Districts", + "tags": [ + "Untags" + ], + "summary": "Delete Districts", + "parameters": [ + { + "type": "integer", + "description": "Districts ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/education-history": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Education History for authenticated user", + "tags": [ + "Education History" + ], + "summary": "Get all Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "educationLevel", + "in": "query" + }, + { + "type": "integer", + "name": "graduationYear", + "in": "query" + }, + { + "type": "string", + "name": "major", + "in": "query" + }, + { + "type": "string", + "name": "schoolName", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Education History", + "tags": [ + "Education History" + ], + "summary": "Create Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.EducationHistoryCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/education-history/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Education History", + "tags": [ + "Education History" + ], + "summary": "Get one Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Education History", + "tags": [ + "Education History" + ], + "summary": "Update Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.EducationHistoryUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Education History", + "tags": [ + "Education History" + ], + "summary": "Delete Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/education-history/{id}/certificate": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for upload certificate image for Education History", + "tags": [ + "Education History" + ], + "summary": "Upload Certificate for Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "file", + "description": "Certificate image file", + "name": "certificate", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/feedbacks": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "Get all Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "commentFromEmail", + "in": "query" + }, + { + "type": "string", + "name": "commentFromName", + "in": "query" + }, + { + "type": "string", + "name": "endDate", + "in": "query" + }, + { + "type": "string", + "name": "message", + "in": "query" + }, + { + "type": "string", + "name": "startDate", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "Create Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.FeedbacksCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/feedbacks/statistic/monthly": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for FeedbackMonthlyStats of Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "FeedbackMonthlyStats Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "year", + "name": "year", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/feedbacks/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "Get one Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Feedbacks ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "update Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.FeedbacksUpdateRequest" + } + }, + { + "type": "integer", + "description": "Feedbacks ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "delete Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Feedbacks ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Get all MagazineFiles", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Create MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Magazine File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Get one MagazineFiles", + "parameters": [ + { + "type": "integer", + "description": "MagazineFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Update MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MagazineFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "delete MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MagazineFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files/{magazineId}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Create MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Magazine file title", + "name": "title", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Magazine file description", + "name": "description", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Magazine ID", + "name": "magazineId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Magazines", + "tags": [ + "Magazines" + ], + "summary": "Get all Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "createdById", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "string", + "name": "pageUrl", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "thumbnailPath", + "in": "query" + }, + { + "type": "string", + "name": "thumbnailUrl", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Magazines", + "tags": [ + "Magazines" + ], + "summary": "Create Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MagazinesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines/thumbnail/viewer/{thumbnailName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for View Thumbnail of Magazines", + "tags": [ + "Magazines" + ], + "summary": "Viewer Magazines Thumbnail", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Magazines Thumbnail Name", + "name": "thumbnailName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines/thumbnail/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Save Thumbnail of Magazines", + "produces": [ + "application/json" + ], + "tags": [ + "Magazines" + ], + "summary": "Save Thumbnail Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Magazine ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "file", + "description": "Upload thumbnail", + "name": "files", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Magazines", + "tags": [ + "Magazines" + ], + "summary": "Get one Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Magazines ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Magazines", + "tags": [ + "Magazines" + ], + "summary": "Update Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Magazines ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MagazinesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Magazines", + "tags": [ + "Magazines" + ], + "summary": "Delete Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Magazines ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-menus": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Get all MasterMenus", + "parameters": [ + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "integer", + "name": "moduleId", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "parentMenuId", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Create MasterMenus", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MasterMenusCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-menus/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Get one MasterMenus", + "parameters": [ + { + "type": "integer", + "description": "MasterMenus ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Update MasterMenus", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterMenus ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Delete MasterMenus", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterMenus ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-modules": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Get all MasterModules", + "parameters": [ + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Create MasterModules", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MasterModulesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-modules/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Get one MasterModules", + "parameters": [ + { + "type": "integer", + "description": "MasterModules ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Update MasterModules", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterModules ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MasterModulesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Delete MasterModules", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterModules ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-statuses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Get all MasterStatuses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Create MasterStatuses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/master-statuses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Get one MasterStatuses", + "parameters": [ + { + "type": "integer", + "description": "MasterStatuses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Update MasterStatuses", + "parameters": [ + { + "type": "integer", + "description": "MasterStatuses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Delete MasterStatuses", + "parameters": [ + { + "type": "integer", + "description": "MasterStatuses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/provinces": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Provinces", + "tags": [ + "Untags" + ], + "summary": "Get all Provinces", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Provinces", + "tags": [ + "Untags" + ], + "summary": "Create Provinces", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/provinces/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Provinces", + "tags": [ + "Untags" + ], + "summary": "Get one Provinces", + "parameters": [ + { + "type": "integer", + "description": "Provinces ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Provinces", + "tags": [ + "Untags" + ], + "summary": "Update Provinces", + "parameters": [ + { + "type": "integer", + "description": "Provinces ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Provinces", + "tags": [ + "Untags" + ], + "summary": "Delete Provinces", + "parameters": [ + { + "type": "integer", + "description": "Provinces ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/research-journals": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Research Journals for authenticated user", + "tags": [ + "Research Journals" + ], + "summary": "Get all Research Journals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "journalTitle", + "in": "query" + }, + { + "type": "integer", + "name": "publishedYear", + "in": "query" + }, + { + "type": "string", + "name": "publisher", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Create Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ResearchJournalsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/research-journals/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Get one Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Research Journal ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Update Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Research Journal ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ResearchJournalsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Delete Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Research Journal ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/subscription": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Subscription", + "tags": [ + "Subscription" + ], + "summary": "Get all Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "email", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Subscription", + "tags": [ + "Subscription" + ], + "summary": "Create Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SubscriptionCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/subscription/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Subscription", + "tags": [ + "Subscription" + ], + "summary": "Get one Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Subscription ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Subscription", + "tags": [ + "Subscription" + ], + "summary": "update Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SubscriptionUpdateRequest" + } + }, + { + "type": "integer", + "description": "Subscription ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Subscription", + "tags": [ + "Subscription" + ], + "summary": "delete Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Subscription ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Get all UserLevels", + "parameters": [ + { + "type": "integer", + "name": "levelNumber", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "parentLevelId", + "in": "query" + }, + { + "type": "integer", + "name": "provinceId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Create UserLevels", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels/alias/{alias}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Get one UserLevels", + "parameters": [ + { + "type": "string", + "description": "UserLevels Alias", + "name": "alias", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels/enable-approval": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Enable Approval of Article", + "tags": [ + "UserLevels" + ], + "summary": "EnableApproval Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelsApprovalRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Get one UserLevels", + "parameters": [ + { + "type": "integer", + "description": "UserLevels ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "update UserLevels", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelsUpdateRequest" + } + }, + { + "type": "integer", + "description": "UserLevels ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "delete UserLevels", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "UserLevels ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user-role-accesses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "Get all UserRoleAccesses", + "parameters": [ + { + "type": "boolean", + "name": "isActive", + "in": "query", + "required": true + }, + { + "type": "integer", + "name": "menuId", + "in": "query", + "required": true + }, + { + "type": "integer", + "name": "userRoleId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "Create UserRoleAccesses", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRoleAccessesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-role-accesses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "Get one UserRoleAccesses", + "parameters": [ + { + "type": "integer", + "description": "UserRoleAccesses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "update UserRoleAccesses", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRoleAccessesUpdateRequest" + } + }, + { + "type": "integer", + "description": "UserRoleAccesses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "delete UserRoleAccesses", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "UserRoleAccesses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-role-level-details": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "Get all UserRoleLevelDetails", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "Create UserRoleLevelDetails", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user-role-level-details/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "Get one UserRoleLevelDetails", + "parameters": [ + { + "type": "integer", + "description": "UserRoleLevelDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "update UserRoleLevelDetails", + "parameters": [ + { + "type": "integer", + "description": "UserRoleLevelDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "delete UserRoleLevelDetails", + "parameters": [ + { + "type": "integer", + "description": "UserRoleLevelDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user-roles": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "Get all UserRoles", + "parameters": [ + { + "type": "string", + "name": "code", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "userLevelId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "Create UserRoles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRolesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-roles/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "Get one UserRoles", + "parameters": [ + { + "type": "integer", + "description": "UserRoles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "update UserRoles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRolesUpdateRequest" + } + }, + { + "type": "integer", + "description": "UserRoles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "delete UserRoles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "UserRoles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Users", + "tags": [ + "Users" + ], + "summary": "Get all Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "email", + "in": "query" + }, + { + "type": "string", + "name": "fullname", + "in": "query" + }, + { + "type": "string", + "name": "genderType", + "in": "query" + }, + { + "type": "string", + "name": "identityGroup", + "in": "query" + }, + { + "type": "string", + "name": "identityGroupNumber", + "in": "query" + }, + { + "type": "string", + "name": "identityNumber", + "in": "query" + }, + { + "type": "string", + "name": "identityType", + "in": "query" + }, + { + "type": "string", + "name": "phoneNumber", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "userRoleId", + "in": "query" + }, + { + "type": "string", + "name": "username", + "in": "query" + }, + { + "type": "string", + "name": "workType", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Users", + "tags": [ + "Users" + ], + "summary": "Create Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UsersCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/detail/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Users", + "tags": [ + "Users" + ], + "summary": "Get one Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Users ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/email-validation": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Email Validation Users", + "tags": [ + "Users" + ], + "summary": "EmailValidation Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserEmailValidationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/forgot-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ForgotPassword Users", + "tags": [ + "Users" + ], + "summary": "ForgotPassword Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserForgotPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/info": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ShowUserInfo", + "tags": [ + "Users" + ], + "summary": "ShowInfo Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/login": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Login Users", + "tags": [ + "Users" + ], + "summary": "Login Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLogin" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/otp-request": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpRequest Users", + "tags": [ + "Users" + ], + "summary": "OtpRequest Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/otp-validation": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpValidation Users", + "tags": [ + "Users" + ], + "summary": "OtpValidation Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpValidation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/pareto-login": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ParetoLogin Users", + "tags": [ + "Users" + ], + "summary": "ParetoLogin Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLogin" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/reset-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ResetPassword Users", + "tags": [ + "Users" + ], + "summary": "ResetPassword Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserResetPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/save-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for SavePassword Users", + "tags": [ + "Users" + ], + "summary": "SavePassword Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserSavePassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/setup-email": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Setup Email Users", + "tags": [ + "Users" + ], + "summary": "SetupEmail Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserEmailValidationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/username/{username}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Users", + "tags": [ + "Users" + ], + "summary": "Get one Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Username", + "name": "username", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Users", + "tags": [ + "Users" + ], + "summary": "update Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Users ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UsersUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Users", + "tags": [ + "Users" + ], + "summary": "delete Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Users ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/work-history": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Work History for authenticated user", + "tags": [ + "Work History" + ], + "summary": "Get all Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "companyName", + "in": "query" + }, + { + "type": "boolean", + "description": "filter for current job (EndDate is null)", + "name": "isCurrent", + "in": "query" + }, + { + "type": "string", + "name": "jobTitle", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Work History", + "tags": [ + "Work History" + ], + "summary": "Create Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.WorkHistoryCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/work-history/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Work History", + "tags": [ + "Work History" + ], + "summary": "Get one Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Work History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Work History", + "tags": [ + "Work History" + ], + "summary": "Update Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Work History ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.WorkHistoryUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Work History", + "tags": [ + "Work History" + ], + "summary": "Delete Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Work History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + } + }, + "definitions": { + "paginator.Pagination": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "nextPage": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "previousPage": { + "type": "integer" + }, + "sort": { + "type": "string" + }, + "sortBy": { + "type": "string" + }, + "totalPage": { + "type": "integer" + } + } + }, + "request.ActivityLogsCreateRequest": { + "type": "object", + "required": [ + "activityTypeId", + "url" + ], + "properties": { + "activityTypeId": { + "type": "integer" + }, + "articleId": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "userId": { + "type": "integer" + }, + "visitorIp": { + "type": "string" + } + } + }, + "request.ActivityLogsUpdateRequest": { + "type": "object", + "required": [ + "activityTypeId", + "id", + "url" + ], + "properties": { + "activityTypeId": { + "type": "integer" + }, + "articleId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "userId": { + "type": "integer" + } + } + }, + "request.AdvertisementCreateRequest": { + "type": "object", + "required": [ + "description", + "placement", + "redirectLink", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "placement": { + "type": "string" + }, + "redirectLink": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.AdvertisementUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "placement", + "redirectLink", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "placement": { + "type": "string" + }, + "redirectLink": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.ArticleApprovalsCreateRequest": { + "type": "object", + "required": [ + "articleId", + "message", + "statusId" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ArticleApprovalsUpdateRequest": { + "type": "object", + "required": [ + "articleId", + "id", + "message", + "statusId" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ArticleCategoriesCreateRequest": { + "type": "object", + "required": [ + "description", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "oldCategoryId": { + "type": "integer" + }, + "parentId": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.ArticleCategoriesUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isPublish": { + "type": "boolean" + }, + "parentId": { + "type": "integer" + }, + "publishedAt": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.ArticleCommentsApprovalRequest": { + "type": "object", + "required": [ + "id", + "statusId" + ], + "properties": { + "id": { + "type": "integer" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ArticleCommentsCreateRequest": { + "type": "object", + "required": [ + "articleId", + "message" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "isPublic": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "parentId": { + "type": "integer" + } + } + }, + "request.ArticleCommentsUpdateRequest": { + "type": "object", + "required": [ + "articleId", + "id", + "message" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "isPublic": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "parentId": { + "type": "integer" + } + } + }, + "request.ArticleFilesUpdateRequest": { + "type": "object", + "required": [ + "articleId", + "id", + "isPublish", + "publishedAt", + "statusId" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "fileAlt": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "filePath": { + "type": "string" + }, + "fileThumbnail": { + "type": "string" + }, + "fileUrl": { + "type": "string" + }, + "heightPixel": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isPublish": { + "type": "boolean" + }, + "publishedAt": { + "type": "string" + }, + "size": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "widthPixel": { + "type": "string" + } + } + }, + "request.ArticlesCreateRequest": { + "type": "object", + "required": [ + "categoryIds", + "description", + "htmlDescription", + "slug", + "tags", + "title", + "typeId" + ], + "properties": { + "aiArticleId": { + "type": "integer" + }, + "categoryIds": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "htmlDescription": { + "type": "string" + }, + "isDraft": { + "type": "boolean" + }, + "isPublish": { + "type": "boolean" + }, + "oldId": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + }, + "typeId": { + "type": "integer" + } + } + }, + "request.ArticlesUpdateRequest": { + "type": "object", + "required": [ + "categoryIds", + "description", + "htmlDescription", + "slug", + "tags", + "title", + "typeId" + ], + "properties": { + "aiArticleId": { + "type": "integer" + }, + "categoryIds": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "htmlDescription": { + "type": "string" + }, + "isDraft": { + "type": "boolean" + }, + "isPublish": { + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + }, + "typeId": { + "type": "integer" + } + } + }, + "request.CitiesCreateRequest": { + "type": "object", + "required": [ + "city_name", + "prov_id" + ], + "properties": { + "city_name": { + "type": "string" + }, + "prov_id": { + "type": "integer" + } + } + }, + "request.CitiesUpdateRequest": { + "type": "object", + "required": [ + "city_name", + "id", + "prov_id" + ], + "properties": { + "city_name": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "prov_id": { + "type": "integer" + } + } + }, + "request.CustomStaticPagesCreateRequest": { + "type": "object", + "required": [ + "htmlBody", + "slug", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "htmlBody": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.CustomStaticPagesUpdateRequest": { + "type": "object", + "required": [ + "htmlBody", + "id", + "slug", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "htmlBody": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "request.EducationHistoryCreateRequest": { + "type": "object", + "required": [ + "educationLevel", + "graduationYear", + "major", + "schoolName" + ], + "properties": { + "certificateImage": { + "type": "string" + }, + "educationLevel": { + "type": "string", + "maxLength": 100, + "minLength": 2 + }, + "graduationYear": { + "type": "integer", + "maximum": 2030, + "minimum": 1950 + }, + "major": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "schoolName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.EducationHistoryUpdateRequest": { + "type": "object", + "required": [ + "educationLevel", + "graduationYear", + "major", + "schoolName" + ], + "properties": { + "certificateImage": { + "type": "string" + }, + "educationLevel": { + "type": "string", + "maxLength": 100, + "minLength": 2 + }, + "graduationYear": { + "type": "integer", + "maximum": 2030, + "minimum": 1950 + }, + "major": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "schoolName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.FeedbacksCreateRequest": { + "type": "object", + "required": [ + "commentFromEmail", + "commentFromName", + "message" + ], + "properties": { + "commentFromEmail": { + "type": "string" + }, + "commentFromName": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "request.FeedbacksUpdateRequest": { + "type": "object", + "required": [ + "commentFromEmail", + "commentFromName", + "id", + "message" + ], + "properties": { + "commentFromEmail": { + "type": "string" + }, + "commentFromName": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "message": { + "type": "string" + } + } + }, + "request.MagazinesCreateRequest": { + "type": "object", + "required": [ + "description", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "isPublish": { + "type": "boolean" + }, + "pageUrl": { + "type": "string" + }, + "publishedAt": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "thumbnailPath": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.MagazinesUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isPublish": { + "type": "boolean" + }, + "pageUrl": { + "type": "string" + }, + "publishedAt": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "thumbnailPath": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.MasterMenusCreateRequest": { + "type": "object", + "required": [ + "description", + "group", + "moduleId", + "name", + "statusId" + ], + "properties": { + "description": { + "type": "string" + }, + "group": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "moduleId": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parentMenuId": { + "type": "integer" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.MasterModulesCreateRequest": { + "type": "object", + "required": [ + "description", + "name", + "pathUrl", + "statusId" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pathUrl": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.MasterModulesUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "name", + "pathUrl", + "statusId" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "pathUrl": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ResearchJournalsCreateRequest": { + "type": "object", + "required": [ + "journalTitle", + "journalUrl", + "publisher" + ], + "properties": { + "journalTitle": { + "type": "string", + "maxLength": 500, + "minLength": 2 + }, + "journalUrl": { + "type": "string" + }, + "publishedDate": { + "type": "string" + }, + "publisher": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.ResearchJournalsUpdateRequest": { + "type": "object", + "required": [ + "journalTitle", + "journalUrl", + "publisher" + ], + "properties": { + "journalTitle": { + "type": "string", + "maxLength": 500, + "minLength": 2 + }, + "journalUrl": { + "type": "string" + }, + "publishedDate": { + "type": "string" + }, + "publisher": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.SubscriptionCreateRequest": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string" + } + } + }, + "request.SubscriptionUpdateRequest": { + "type": "object", + "required": [ + "email", + "id" + ], + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + } + } + }, + "request.UserEmailValidationRequest": { + "type": "object", + "properties": { + "newEmail": { + "type": "string" + }, + "oldEmail": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "request.UserForgotPassword": { + "type": "object", + "properties": { + "username": { + "type": "string" + } + } + }, + "request.UserLevelsApprovalRequest": { + "type": "object", + "required": [ + "ids", + "isApprovalActive" + ], + "properties": { + "ids": { + "type": "string" + }, + "isApprovalActive": { + "type": "boolean" + } + } + }, + "request.UserLevelsCreateRequest": { + "type": "object", + "required": [ + "aliasName", + "levelNumber", + "name" + ], + "properties": { + "aliasName": { + "type": "string" + }, + "group": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "isApprovalActive": { + "type": "boolean" + }, + "levelNumber": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parentLevelId": { + "type": "integer" + }, + "provinceId": { + "type": "integer" + } + } + }, + "request.UserLevelsUpdateRequest": { + "type": "object", + "required": [ + "aliasName", + "levelNumber", + "name" + ], + "properties": { + "aliasName": { + "type": "string" + }, + "group": { + "type": "string" + }, + "isApprovalActive": { + "type": "boolean" + }, + "levelNumber": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parentLevelId": { + "type": "integer" + }, + "provinceId": { + "type": "integer" + } + } + }, + "request.UserLogin": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "refreshToken": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "request.UserOtpRequest": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "request.UserOtpValidation": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "otpCode": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "request.UserResetPassword": { + "type": "object", + "properties": { + "codeRequest": { + "type": "string" + }, + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "request.UserRoleAccessesCreateRequest": { + "type": "object", + "required": [ + "isAdminEnabled", + "isApprovalEnabled", + "isDeleteEnabled", + "isInsertEnabled", + "isUpdateEnabled", + "isViewEnabled", + "menuId" + ], + "properties": { + "isAdminEnabled": { + "type": "boolean" + }, + "isApprovalEnabled": { + "type": "boolean" + }, + "isDeleteEnabled": { + "type": "boolean" + }, + "isInsertEnabled": { + "type": "boolean" + }, + "isUpdateEnabled": { + "type": "boolean" + }, + "isViewEnabled": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + } + } + }, + "request.UserRoleAccessesUpdateRequest": { + "type": "object", + "required": [ + "id", + "is_admin_enabled", + "is_approval_enabled", + "is_delete_enabled", + "is_insert_enabled", + "is_update_enabled", + "is_view_enabled", + "menu_id", + "user_role_id" + ], + "properties": { + "id": { + "type": "integer" + }, + "is_admin_enabled": { + "type": "boolean" + }, + "is_approval_enabled": { + "type": "boolean" + }, + "is_delete_enabled": { + "type": "boolean" + }, + "is_insert_enabled": { + "type": "boolean" + }, + "is_update_enabled": { + "type": "boolean" + }, + "is_view_enabled": { + "type": "boolean" + }, + "menu_id": { + "type": "integer" + }, + "user_role_id": { + "type": "integer" + } + } + }, + "request.UserRolesCreateRequest": { + "type": "object", + "required": [ + "code", + "description", + "name", + "statusId", + "userLevelIds", + "userRoleAccess" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "userLevelIds": { + "type": "array", + "items": { + "type": "integer" + } + }, + "userRoleAccess": { + "type": "array", + "items": { + "$ref": "#/definitions/request.UserRoleAccessesCreateRequest" + } + } + } + }, + "request.UserRolesUpdateRequest": { + "type": "object", + "required": [ + "code", + "description", + "level_number", + "name", + "status_id", + "userLevelIds" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "level_number": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "status_id": { + "type": "integer" + }, + "userLevelIds": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "request.UserSavePassword": { + "type": "object", + "properties": { + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "request.UsersCreateRequest": { + "type": "object", + "required": [ + "email", + "fullname", + "password", + "userLevelId", + "userRoleId", + "username" + ], + "properties": { + "address": { + "type": "string" + }, + "dateOfBirth": { + "type": "string" + }, + "degree": { + "type": "string" + }, + "email": { + "type": "string" + }, + "fullname": { + "type": "string" + }, + "genderType": { + "type": "string" + }, + "identityGroup": { + "type": "string" + }, + "identityGroupNumber": { + "type": "string" + }, + "identityNumber": { + "type": "string" + }, + "identityType": { + "type": "string" + }, + "lastEducation": { + "type": "string" + }, + "lastJobTitle": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phoneNumber": { + "type": "string" + }, + "userLevelId": { + "type": "integer" + }, + "userRoleId": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "whatsappNumber": { + "type": "string" + }, + "workType": { + "type": "string" + } + } + }, + "request.UsersUpdateRequest": { + "type": "object", + "required": [ + "email", + "fullname", + "userLevelId", + "userRoleId", + "username" + ], + "properties": { + "address": { + "type": "string" + }, + "dateOfBirth": { + "type": "string" + }, + "degree": { + "type": "string" + }, + "email": { + "type": "string" + }, + "fullname": { + "type": "string" + }, + "genderType": { + "type": "string" + }, + "identityGroup": { + "type": "string" + }, + "identityGroupNumber": { + "type": "string" + }, + "identityNumber": { + "type": "string" + }, + "identityType": { + "type": "string" + }, + "lastEducation": { + "type": "string" + }, + "lastJobTitle": { + "type": "string" + }, + "phoneNumber": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + }, + "userRoleId": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "whatsappNumber": { + "type": "string" + }, + "workType": { + "type": "string" + } + } + }, + "request.WorkHistoryCreateRequest": { + "type": "object", + "required": [ + "companyName", + "jobTitle", + "startDate" + ], + "properties": { + "companyName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "endDate": { + "type": "string" + }, + "jobTitle": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "startDate": { + "type": "string" + } + } + }, + "request.WorkHistoryUpdateRequest": { + "type": "object", + "required": [ + "companyName", + "jobTitle", + "startDate" + ], + "properties": { + "companyName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "endDate": { + "type": "string" + }, + "jobTitle": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "startDate": { + "type": "string" + } + } + }, + "response.BadRequestError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 400 + }, + "message": { + "type": "string", + "example": "bad request" + }, + "success": { + "type": "boolean", + "example": false + } + } + }, + "response.InternalServerError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 500 + }, + "message": { + "type": "string", + "example": "internal server error" + }, + "success": { + "type": "boolean", + "example": false + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "data": {}, + "messages": { + "type": "array", + "items": {} + }, + "meta": {}, + "success": { + "type": "boolean", + "example": true + } + } + }, + "response.UnauthorizedError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 401 + }, + "message": { + "type": "string", + "example": "unauthorized access" + }, + "success": { + "type": "boolean", + "example": false + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "", + Description: "", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json new file mode 100644 index 0000000..c8d412b --- /dev/null +++ b/docs/swagger/swagger.json @@ -0,0 +1,12663 @@ +{ + "swagger": "2.0", + "info": { + "contact": {} + }, + "paths": { + "/activity-logs": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Get all ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "activityTypeId", + "in": "query" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "string", + "name": "url", + "in": "query" + }, + { + "type": "integer", + "name": "userId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Create ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ActivityLogsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/activity-logs/detail/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Get one ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ActivityLogs ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/activity-logs/statistics": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for get activity stats ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "Get activity stats ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/activity-logs/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "update ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ActivityLogsUpdateRequest" + } + }, + { + "type": "integer", + "description": "ActivityLogs ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ActivityLogs", + "tags": [ + "ActivityLogs" + ], + "summary": "delete ActivityLogs", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ActivityLogs ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Get all Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "string", + "name": "placement", + "in": "query" + }, + { + "type": "string", + "name": "redirectLink", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Create Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AdvertisementCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/publish/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Update Publish Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Update Publish Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Advertisement Publish Status", + "name": "isPublish", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/upload/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Upload File Advertisement", + "produces": [ + "application/json" + ], + "tags": [ + "Advertisement" + ], + "summary": "Upload Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload file", + "name": "file", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Viewer Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Viewer Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Content File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/advertisement/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "Get one Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "update Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AdvertisementUpdateRequest" + } + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Advertisement", + "tags": [ + "Advertisement" + ], + "summary": "delete Advertisement", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Advertisement ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-approvals": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "Get all ArticleApprovals", + "parameters": [ + { + "type": "integer", + "name": "approvalAtLevel", + "in": "query" + }, + { + "type": "integer", + "name": "approvalBy", + "in": "query" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "string", + "name": "message", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "Create ArticleApprovals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleApprovalsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-approvals/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "Get one ArticleApprovals", + "parameters": [ + { + "type": "integer", + "description": "ArticleApprovals ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "update ArticleApprovals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleApprovalsUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleApprovals ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleApprovals", + "tags": [ + "ArticleApprovals" + ], + "summary": "delete ArticleApprovals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleApprovals ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get all ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "name": "UserLevelId", + "in": "query" + }, + { + "type": "integer", + "name": "UserLevelNumber", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "integer", + "name": "parentId", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Create ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCategoriesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/old/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get one ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleCategories Old ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/slug/{slug}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get one ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "ArticleCategories Slug", + "name": "slug", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/thumbnail/viewer/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for View Thumbnail of ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Viewer ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/thumbnail/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Upload ArticleCategories Thumbnail", + "produces": [ + "application/json" + ], + "tags": [ + "Article Categories" + ], + "summary": "Upload ArticleCategories Thumbnail", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload thumbnail", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-categories/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "Get one ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "update ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCategoriesUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleCategories", + "tags": [ + "Article Categories" + ], + "summary": "delete ArticleCategories", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategories ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-category-details": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "Get all ArticleCategoryDetails", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "Create ArticleCategoryDetails", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/article-category-details/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "Get one ArticleCategoryDetails", + "parameters": [ + { + "type": "integer", + "description": "ArticleCategoryDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "update ArticleCategoryDetails", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategoryDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleCategoryDetails", + "tags": [ + "Untags" + ], + "summary": "delete ArticleCategoryDetails", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleCategoryDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/article-comments": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Get all ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "integer", + "name": "commentFrom", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublic", + "in": "query" + }, + { + "type": "string", + "name": "message", + "in": "query" + }, + { + "type": "integer", + "name": "parentId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Create ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCommentsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-comments/approval": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Approval ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Approval ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCommentsApprovalRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-comments/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "Get one ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleComments ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "update ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleCommentsUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleComments ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleComments", + "tags": [ + "ArticleComments" + ], + "summary": "delete ArticleComments", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleComments ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Get all ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "articleId", + "in": "query" + }, + { + "type": "string", + "name": "fileName", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/upload-status/{uploadId}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for GetUploadStatus ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "GetUploadStatus ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Upload ID of ArticleFiles", + "name": "uploadId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Viewer ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Viewer ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Article File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/{articleId}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create ArticleFiles", + "produces": [ + "application/json" + ], + "tags": [ + "Article Files" + ], + "summary": "Upload ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Article ID", + "name": "articleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/article-files/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Get one ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "ArticleFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Update ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticleFilesUpdateRequest" + } + }, + { + "type": "integer", + "description": "ArticleFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete ArticleFiles", + "tags": [ + "Article Files" + ], + "summary": "Delete ArticleFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "ArticleFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Articles", + "tags": [ + "Articles" + ], + "summary": "Get all Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header", + "required": true + }, + { + "type": "string", + "name": "category", + "in": "query" + }, + { + "type": "integer", + "name": "categoryId", + "in": "query" + }, + { + "type": "integer", + "name": "createdById", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isBanner", + "in": "query" + }, + { + "type": "boolean", + "name": "isDraft", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "tags", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "typeId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Articles", + "tags": [ + "Articles" + ], + "summary": "Create Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticlesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/banner/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Update Banner Articles", + "tags": [ + "Articles" + ], + "summary": "Update Banner Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Articles Banner Status", + "name": "isBanner", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/old-id/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Articles", + "tags": [ + "Articles" + ], + "summary": "Get one Articles", + "parameters": [ + { + "type": "integer", + "description": "Articles Old ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/publish-scheduling": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Publish Schedule of Article", + "tags": [ + "Articles" + ], + "summary": "PublishScheduling Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "article id", + "name": "id", + "in": "query" + }, + { + "type": "string", + "description": "publish date", + "name": "date", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/statistic/monthly": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticleMonthlyStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticleMonthlyStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "year", + "name": "year", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/statistic/summary": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Summary Stats of Article", + "tags": [ + "Articles" + ], + "summary": "SummaryStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/statistic/user-levels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ArticlePerUserLevelStats of Article", + "tags": [ + "Articles" + ], + "summary": "ArticlePerUserLevelStats Articles", + "parameters": [ + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "string", + "description": "start date", + "name": "startDate", + "in": "query" + }, + { + "type": "string", + "description": "start date", + "name": "endDate", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/thumbnail/viewer/{thumbnailName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for View Thumbnail of Article", + "tags": [ + "Articles" + ], + "summary": "Viewer Articles Thumbnail", + "parameters": [ + { + "type": "string", + "description": "Articles Thumbnail Name", + "name": "thumbnailName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/thumbnail/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Save Thumbnail of Articles", + "produces": [ + "application/json" + ], + "tags": [ + "Articles" + ], + "summary": "Save Thumbnail Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload thumbnail", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/articles/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Articles", + "tags": [ + "Articles" + ], + "summary": "Get one Articles", + "parameters": [ + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Articles", + "tags": [ + "Articles" + ], + "summary": "Update Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ArticlesUpdateRequest" + } + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Articles", + "tags": [ + "Articles" + ], + "summary": "Delete Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Articles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/cities": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Cities", + "tags": [ + "Untags" + ], + "summary": "Get all Cities", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Cities", + "tags": [ + "Untags" + ], + "summary": "Create Cities", + "parameters": [ + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CitiesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/cities/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Cities", + "tags": [ + "Untags" + ], + "summary": "Get one Cities", + "parameters": [ + { + "type": "integer", + "description": "Cities ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Cities", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Untags" + ], + "summary": "Update Cities", + "parameters": [ + { + "type": "integer", + "description": "Cities ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CitiesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Cities", + "tags": [ + "Untags" + ], + "summary": "Delete Cities", + "parameters": [ + { + "type": "integer", + "description": "Cities ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/custom-static-pages": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Get all CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "string", + "name": "htmlBody", + "in": "query" + }, + { + "type": "string", + "name": "slug", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Create CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CustomStaticPagesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/custom-static-pages/slug/{slug}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Get one CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "CustomStaticPages Slug", + "name": "slug", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/custom-static-pages/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "Get one CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "CustomStaticPages ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "update CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CustomStaticPagesUpdateRequest" + } + }, + { + "type": "integer", + "description": "CustomStaticPages ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete CustomStaticPages", + "tags": [ + "CustomStaticPages" + ], + "summary": "delete CustomStaticPages", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "CustomStaticPages ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/districts": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Districts", + "tags": [ + "Untags" + ], + "summary": "Get all Districts", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Districts", + "tags": [ + "Untags" + ], + "summary": "Create Districts", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/districts/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Districts", + "tags": [ + "Untags" + ], + "summary": "Get one Districts", + "parameters": [ + { + "type": "integer", + "description": "Districts ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Districts", + "tags": [ + "Untags" + ], + "summary": "Update Districts", + "parameters": [ + { + "type": "integer", + "description": "Districts ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Districts", + "tags": [ + "Untags" + ], + "summary": "Delete Districts", + "parameters": [ + { + "type": "integer", + "description": "Districts ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/education-history": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Education History for authenticated user", + "tags": [ + "Education History" + ], + "summary": "Get all Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "educationLevel", + "in": "query" + }, + { + "type": "integer", + "name": "graduationYear", + "in": "query" + }, + { + "type": "string", + "name": "major", + "in": "query" + }, + { + "type": "string", + "name": "schoolName", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Education History", + "tags": [ + "Education History" + ], + "summary": "Create Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.EducationHistoryCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/education-history/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Education History", + "tags": [ + "Education History" + ], + "summary": "Get one Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Education History", + "tags": [ + "Education History" + ], + "summary": "Update Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.EducationHistoryUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Education History", + "tags": [ + "Education History" + ], + "summary": "Delete Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/education-history/{id}/certificate": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for upload certificate image for Education History", + "tags": [ + "Education History" + ], + "summary": "Upload Certificate for Education History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Education History ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "file", + "description": "Certificate image file", + "name": "certificate", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/feedbacks": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "Get all Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "commentFromEmail", + "in": "query" + }, + { + "type": "string", + "name": "commentFromName", + "in": "query" + }, + { + "type": "string", + "name": "endDate", + "in": "query" + }, + { + "type": "string", + "name": "message", + "in": "query" + }, + { + "type": "string", + "name": "startDate", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "Create Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.FeedbacksCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/feedbacks/statistic/monthly": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for FeedbackMonthlyStats of Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "FeedbackMonthlyStats Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "type": "integer", + "description": "year", + "name": "year", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/feedbacks/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "Get one Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Feedbacks ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "update Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.FeedbacksUpdateRequest" + } + }, + { + "type": "integer", + "description": "Feedbacks ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Feedbacks", + "tags": [ + "Feedbacks" + ], + "summary": "delete Feedbacks", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Feedbacks ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Get all MagazineFiles", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files/viewer/{filename}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Create MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Magazine File Name", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Get one MagazineFiles", + "parameters": [ + { + "type": "integer", + "description": "MagazineFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Update MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MagazineFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "delete MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MagazineFiles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazine-files/{magazineId}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MagazineFiles", + "tags": [ + "Magazine Files" + ], + "summary": "Create MagazineFiles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "file", + "description": "Upload file", + "name": "files", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Magazine file title", + "name": "title", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Magazine file description", + "name": "description", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Magazine ID", + "name": "magazineId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Magazines", + "tags": [ + "Magazines" + ], + "summary": "Get all Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "name": "createdById", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "boolean", + "name": "isPublish", + "in": "query" + }, + { + "type": "string", + "name": "pageUrl", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "string", + "name": "thumbnailPath", + "in": "query" + }, + { + "type": "string", + "name": "thumbnailUrl", + "in": "query" + }, + { + "type": "string", + "name": "title", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Magazines", + "tags": [ + "Magazines" + ], + "summary": "Create Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MagazinesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines/thumbnail/viewer/{thumbnailName}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for View Thumbnail of Magazines", + "tags": [ + "Magazines" + ], + "summary": "Viewer Magazines Thumbnail", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Magazines Thumbnail Name", + "name": "thumbnailName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines/thumbnail/{id}": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Save Thumbnail of Magazines", + "produces": [ + "application/json" + ], + "tags": [ + "Magazines" + ], + "summary": "Save Thumbnail Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Magazine ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "file", + "description": "Upload thumbnail", + "name": "files", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/magazines/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Magazines", + "tags": [ + "Magazines" + ], + "summary": "Get one Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Magazines ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Magazines", + "tags": [ + "Magazines" + ], + "summary": "Update Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Magazines ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MagazinesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Magazines", + "tags": [ + "Magazines" + ], + "summary": "Delete Magazines", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Magazines ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-menus": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Get all MasterMenus", + "parameters": [ + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "integer", + "name": "moduleId", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "parentMenuId", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Create MasterMenus", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MasterMenusCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-menus/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Get one MasterMenus", + "parameters": [ + { + "type": "integer", + "description": "MasterMenus ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Update MasterMenus", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterMenus ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MasterMenus", + "tags": [ + "MasterMenus" + ], + "summary": "Delete MasterMenus", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterMenus ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-modules": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Get all MasterModules", + "parameters": [ + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Create MasterModules", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MasterModulesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-modules/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Get one MasterModules", + "parameters": [ + { + "type": "integer", + "description": "MasterModules ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Update MasterModules", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterModules ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MasterModulesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MasterModules", + "tags": [ + "MasterModules" + ], + "summary": "Delete MasterModules", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "MasterModules ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/master-statuses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Get all MasterStatuses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Create MasterStatuses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/master-statuses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Get one MasterStatuses", + "parameters": [ + { + "type": "integer", + "description": "MasterStatuses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Update MasterStatuses", + "parameters": [ + { + "type": "integer", + "description": "MasterStatuses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MasterStatuses", + "tags": [ + "Untags" + ], + "summary": "Delete MasterStatuses", + "parameters": [ + { + "type": "integer", + "description": "MasterStatuses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/provinces": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Provinces", + "tags": [ + "Untags" + ], + "summary": "Get all Provinces", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Provinces", + "tags": [ + "Untags" + ], + "summary": "Create Provinces", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/provinces/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Provinces", + "tags": [ + "Untags" + ], + "summary": "Get one Provinces", + "parameters": [ + { + "type": "integer", + "description": "Provinces ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Provinces", + "tags": [ + "Untags" + ], + "summary": "Update Provinces", + "parameters": [ + { + "type": "integer", + "description": "Provinces ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Provinces", + "tags": [ + "Untags" + ], + "summary": "Delete Provinces", + "parameters": [ + { + "type": "integer", + "description": "Provinces ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/research-journals": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Research Journals for authenticated user", + "tags": [ + "Research Journals" + ], + "summary": "Get all Research Journals", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "journalTitle", + "in": "query" + }, + { + "type": "integer", + "name": "publishedYear", + "in": "query" + }, + { + "type": "string", + "name": "publisher", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Create Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ResearchJournalsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/research-journals/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Get one Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Research Journal ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Update Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Research Journal ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ResearchJournalsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Research Journal", + "tags": [ + "Research Journals" + ], + "summary": "Delete Research Journal", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Research Journal ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/subscription": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Subscription", + "tags": [ + "Subscription" + ], + "summary": "Get all Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "email", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Subscription", + "tags": [ + "Subscription" + ], + "summary": "Create Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SubscriptionCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/subscription/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Subscription", + "tags": [ + "Subscription" + ], + "summary": "Get one Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Subscription ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Subscription", + "tags": [ + "Subscription" + ], + "summary": "update Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.SubscriptionUpdateRequest" + } + }, + { + "type": "integer", + "description": "Subscription ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Subscription", + "tags": [ + "Subscription" + ], + "summary": "delete Subscription", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Subscription ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Get all UserLevels", + "parameters": [ + { + "type": "integer", + "name": "levelNumber", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "parentLevelId", + "in": "query" + }, + { + "type": "integer", + "name": "provinceId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Create UserLevels", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels/alias/{alias}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Get one UserLevels", + "parameters": [ + { + "type": "string", + "description": "UserLevels Alias", + "name": "alias", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels/enable-approval": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Enable Approval of Article", + "tags": [ + "UserLevels" + ], + "summary": "EnableApproval Articles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelsApprovalRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-levels/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "Get one UserLevels", + "parameters": [ + { + "type": "integer", + "description": "UserLevels ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "update UserLevels", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelsUpdateRequest" + } + }, + { + "type": "integer", + "description": "UserLevels ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserLevels", + "tags": [ + "UserLevels" + ], + "summary": "delete UserLevels", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "UserLevels ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user-role-accesses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "Get all UserRoleAccesses", + "parameters": [ + { + "type": "boolean", + "name": "isActive", + "in": "query", + "required": true + }, + { + "type": "integer", + "name": "menuId", + "in": "query", + "required": true + }, + { + "type": "integer", + "name": "userRoleId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "Create UserRoleAccesses", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRoleAccessesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-role-accesses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "Get one UserRoleAccesses", + "parameters": [ + { + "type": "integer", + "description": "UserRoleAccesses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "update UserRoleAccesses", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRoleAccessesUpdateRequest" + } + }, + { + "type": "integer", + "description": "UserRoleAccesses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserRoleAccesses", + "tags": [ + "UserRoleAccesses" + ], + "summary": "delete UserRoleAccesses", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "UserRoleAccesses ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-role-level-details": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "Get all UserRoleLevelDetails", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "Create UserRoleLevelDetails", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user-role-level-details/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "Get one UserRoleLevelDetails", + "parameters": [ + { + "type": "integer", + "description": "UserRoleLevelDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "update UserRoleLevelDetails", + "parameters": [ + { + "type": "integer", + "description": "UserRoleLevelDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserRoleLevelDetails", + "tags": [ + "Task" + ], + "summary": "delete UserRoleLevelDetails", + "parameters": [ + { + "type": "integer", + "description": "UserRoleLevelDetails ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user-roles": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "Get all UserRoles", + "parameters": [ + { + "type": "string", + "name": "code", + "in": "query" + }, + { + "type": "string", + "name": "description", + "in": "query" + }, + { + "type": "string", + "name": "name", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "userLevelId", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "Create UserRoles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRolesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-roles/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "Get one UserRoles", + "parameters": [ + { + "type": "integer", + "description": "UserRoles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "update UserRoles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserRolesUpdateRequest" + } + }, + { + "type": "integer", + "description": "UserRoles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserRoles", + "tags": [ + "UserRoles" + ], + "summary": "delete UserRoles", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "UserRoles ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Users", + "tags": [ + "Users" + ], + "summary": "Get all Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "email", + "in": "query" + }, + { + "type": "string", + "name": "fullname", + "in": "query" + }, + { + "type": "string", + "name": "genderType", + "in": "query" + }, + { + "type": "string", + "name": "identityGroup", + "in": "query" + }, + { + "type": "string", + "name": "identityGroupNumber", + "in": "query" + }, + { + "type": "string", + "name": "identityNumber", + "in": "query" + }, + { + "type": "string", + "name": "identityType", + "in": "query" + }, + { + "type": "string", + "name": "phoneNumber", + "in": "query" + }, + { + "type": "integer", + "name": "statusId", + "in": "query" + }, + { + "type": "integer", + "name": "userRoleId", + "in": "query" + }, + { + "type": "string", + "name": "username", + "in": "query" + }, + { + "type": "string", + "name": "workType", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Users", + "tags": [ + "Users" + ], + "summary": "Create Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UsersCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/detail/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Users", + "tags": [ + "Users" + ], + "summary": "Get one Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Users ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/email-validation": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Email Validation Users", + "tags": [ + "Users" + ], + "summary": "EmailValidation Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserEmailValidationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/forgot-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ForgotPassword Users", + "tags": [ + "Users" + ], + "summary": "ForgotPassword Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserForgotPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/info": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ShowUserInfo", + "tags": [ + "Users" + ], + "summary": "ShowInfo Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/login": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Login Users", + "tags": [ + "Users" + ], + "summary": "Login Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLogin" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/otp-request": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpRequest Users", + "tags": [ + "Users" + ], + "summary": "OtpRequest Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/otp-validation": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for OtpValidation Users", + "tags": [ + "Users" + ], + "summary": "OtpValidation Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserOtpValidation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/pareto-login": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ParetoLogin Users", + "tags": [ + "Users" + ], + "summary": "ParetoLogin Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLogin" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/reset-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for ResetPassword Users", + "tags": [ + "Users" + ], + "summary": "ResetPassword Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserResetPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/save-password": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for SavePassword Users", + "tags": [ + "Users" + ], + "summary": "SavePassword Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserSavePassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/setup-email": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for Setup Email Users", + "tags": [ + "Users" + ], + "summary": "SetupEmail Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserEmailValidationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/username/{username}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Users", + "tags": [ + "Users" + ], + "summary": "Get one Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Username", + "name": "username", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/users/{id}": { + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Users", + "tags": [ + "Users" + ], + "summary": "update Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Users ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UsersUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Users", + "tags": [ + "Users" + ], + "summary": "delete Users", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Users ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/work-history": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all Work History for authenticated user", + "tags": [ + "Work History" + ], + "summary": "Get all Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "name": "companyName", + "in": "query" + }, + { + "type": "boolean", + "description": "filter for current job (EndDate is null)", + "name": "isCurrent", + "in": "query" + }, + { + "type": "string", + "name": "jobTitle", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create Work History", + "tags": [ + "Work History" + ], + "summary": "Create Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "Bearer \u003cAdd access token here\u003e", + "description": "Insert your access token", + "name": "Authorization", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.WorkHistoryCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/work-history/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one Work History", + "tags": [ + "Work History" + ], + "summary": "Get one Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "integer", + "description": "Work History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update Work History", + "tags": [ + "Work History" + ], + "summary": "Update Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Work History ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.WorkHistoryUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete Work History", + "tags": [ + "Work History" + ], + "summary": "Delete Work History", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Client-Key", + "name": "X-Client-Key", + "in": "header" + }, + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Work History ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + } + }, + "definitions": { + "paginator.Pagination": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "nextPage": { + "type": "integer" + }, + "page": { + "type": "integer" + }, + "previousPage": { + "type": "integer" + }, + "sort": { + "type": "string" + }, + "sortBy": { + "type": "string" + }, + "totalPage": { + "type": "integer" + } + } + }, + "request.ActivityLogsCreateRequest": { + "type": "object", + "required": [ + "activityTypeId", + "url" + ], + "properties": { + "activityTypeId": { + "type": "integer" + }, + "articleId": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "userId": { + "type": "integer" + }, + "visitorIp": { + "type": "string" + } + } + }, + "request.ActivityLogsUpdateRequest": { + "type": "object", + "required": [ + "activityTypeId", + "id", + "url" + ], + "properties": { + "activityTypeId": { + "type": "integer" + }, + "articleId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "url": { + "type": "string" + }, + "userId": { + "type": "integer" + } + } + }, + "request.AdvertisementCreateRequest": { + "type": "object", + "required": [ + "description", + "placement", + "redirectLink", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "placement": { + "type": "string" + }, + "redirectLink": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.AdvertisementUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "placement", + "redirectLink", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "placement": { + "type": "string" + }, + "redirectLink": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.ArticleApprovalsCreateRequest": { + "type": "object", + "required": [ + "articleId", + "message", + "statusId" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ArticleApprovalsUpdateRequest": { + "type": "object", + "required": [ + "articleId", + "id", + "message", + "statusId" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ArticleCategoriesCreateRequest": { + "type": "object", + "required": [ + "description", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "oldCategoryId": { + "type": "integer" + }, + "parentId": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.ArticleCategoriesUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isPublish": { + "type": "boolean" + }, + "parentId": { + "type": "integer" + }, + "publishedAt": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.ArticleCommentsApprovalRequest": { + "type": "object", + "required": [ + "id", + "statusId" + ], + "properties": { + "id": { + "type": "integer" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ArticleCommentsCreateRequest": { + "type": "object", + "required": [ + "articleId", + "message" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "isPublic": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "parentId": { + "type": "integer" + } + } + }, + "request.ArticleCommentsUpdateRequest": { + "type": "object", + "required": [ + "articleId", + "id", + "message" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "isPublic": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "parentId": { + "type": "integer" + } + } + }, + "request.ArticleFilesUpdateRequest": { + "type": "object", + "required": [ + "articleId", + "id", + "isPublish", + "publishedAt", + "statusId" + ], + "properties": { + "articleId": { + "type": "integer" + }, + "fileAlt": { + "type": "string" + }, + "fileName": { + "type": "string" + }, + "filePath": { + "type": "string" + }, + "fileThumbnail": { + "type": "string" + }, + "fileUrl": { + "type": "string" + }, + "heightPixel": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isPublish": { + "type": "boolean" + }, + "publishedAt": { + "type": "string" + }, + "size": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "widthPixel": { + "type": "string" + } + } + }, + "request.ArticlesCreateRequest": { + "type": "object", + "required": [ + "categoryIds", + "description", + "htmlDescription", + "slug", + "tags", + "title", + "typeId" + ], + "properties": { + "aiArticleId": { + "type": "integer" + }, + "categoryIds": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "htmlDescription": { + "type": "string" + }, + "isDraft": { + "type": "boolean" + }, + "isPublish": { + "type": "boolean" + }, + "oldId": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + }, + "typeId": { + "type": "integer" + } + } + }, + "request.ArticlesUpdateRequest": { + "type": "object", + "required": [ + "categoryIds", + "description", + "htmlDescription", + "slug", + "tags", + "title", + "typeId" + ], + "properties": { + "aiArticleId": { + "type": "integer" + }, + "categoryIds": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "htmlDescription": { + "type": "string" + }, + "isDraft": { + "type": "boolean" + }, + "isPublish": { + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "tags": { + "type": "string" + }, + "title": { + "type": "string" + }, + "typeId": { + "type": "integer" + } + } + }, + "request.CitiesCreateRequest": { + "type": "object", + "required": [ + "city_name", + "prov_id" + ], + "properties": { + "city_name": { + "type": "string" + }, + "prov_id": { + "type": "integer" + } + } + }, + "request.CitiesUpdateRequest": { + "type": "object", + "required": [ + "city_name", + "id", + "prov_id" + ], + "properties": { + "city_name": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "prov_id": { + "type": "integer" + } + } + }, + "request.CustomStaticPagesCreateRequest": { + "type": "object", + "required": [ + "htmlBody", + "slug", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "htmlBody": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.CustomStaticPagesUpdateRequest": { + "type": "object", + "required": [ + "htmlBody", + "id", + "slug", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "htmlBody": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "request.EducationHistoryCreateRequest": { + "type": "object", + "required": [ + "educationLevel", + "graduationYear", + "major", + "schoolName" + ], + "properties": { + "certificateImage": { + "type": "string" + }, + "educationLevel": { + "type": "string", + "maxLength": 100, + "minLength": 2 + }, + "graduationYear": { + "type": "integer", + "maximum": 2030, + "minimum": 1950 + }, + "major": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "schoolName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.EducationHistoryUpdateRequest": { + "type": "object", + "required": [ + "educationLevel", + "graduationYear", + "major", + "schoolName" + ], + "properties": { + "certificateImage": { + "type": "string" + }, + "educationLevel": { + "type": "string", + "maxLength": 100, + "minLength": 2 + }, + "graduationYear": { + "type": "integer", + "maximum": 2030, + "minimum": 1950 + }, + "major": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "schoolName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.FeedbacksCreateRequest": { + "type": "object", + "required": [ + "commentFromEmail", + "commentFromName", + "message" + ], + "properties": { + "commentFromEmail": { + "type": "string" + }, + "commentFromName": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "request.FeedbacksUpdateRequest": { + "type": "object", + "required": [ + "commentFromEmail", + "commentFromName", + "id", + "message" + ], + "properties": { + "commentFromEmail": { + "type": "string" + }, + "commentFromName": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "message": { + "type": "string" + } + } + }, + "request.MagazinesCreateRequest": { + "type": "object", + "required": [ + "description", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "isPublish": { + "type": "boolean" + }, + "pageUrl": { + "type": "string" + }, + "publishedAt": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "thumbnailPath": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.MagazinesUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "statusId", + "title" + ], + "properties": { + "createdById": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "isPublish": { + "type": "boolean" + }, + "pageUrl": { + "type": "string" + }, + "publishedAt": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "thumbnailPath": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "request.MasterMenusCreateRequest": { + "type": "object", + "required": [ + "description", + "group", + "moduleId", + "name", + "statusId" + ], + "properties": { + "description": { + "type": "string" + }, + "group": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "moduleId": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parentMenuId": { + "type": "integer" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.MasterModulesCreateRequest": { + "type": "object", + "required": [ + "description", + "name", + "pathUrl", + "statusId" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "pathUrl": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.MasterModulesUpdateRequest": { + "type": "object", + "required": [ + "description", + "id", + "name", + "pathUrl", + "statusId" + ], + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "pathUrl": { + "type": "string" + }, + "statusId": { + "type": "integer" + } + } + }, + "request.ResearchJournalsCreateRequest": { + "type": "object", + "required": [ + "journalTitle", + "journalUrl", + "publisher" + ], + "properties": { + "journalTitle": { + "type": "string", + "maxLength": 500, + "minLength": 2 + }, + "journalUrl": { + "type": "string" + }, + "publishedDate": { + "type": "string" + }, + "publisher": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.ResearchJournalsUpdateRequest": { + "type": "object", + "required": [ + "journalTitle", + "journalUrl", + "publisher" + ], + "properties": { + "journalTitle": { + "type": "string", + "maxLength": 500, + "minLength": 2 + }, + "journalUrl": { + "type": "string" + }, + "publishedDate": { + "type": "string" + }, + "publisher": { + "type": "string", + "maxLength": 255, + "minLength": 2 + } + } + }, + "request.SubscriptionCreateRequest": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string" + } + } + }, + "request.SubscriptionUpdateRequest": { + "type": "object", + "required": [ + "email", + "id" + ], + "properties": { + "email": { + "type": "string" + }, + "id": { + "type": "integer" + } + } + }, + "request.UserEmailValidationRequest": { + "type": "object", + "properties": { + "newEmail": { + "type": "string" + }, + "oldEmail": { + "type": "string" + }, + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "request.UserForgotPassword": { + "type": "object", + "properties": { + "username": { + "type": "string" + } + } + }, + "request.UserLevelsApprovalRequest": { + "type": "object", + "required": [ + "ids", + "isApprovalActive" + ], + "properties": { + "ids": { + "type": "string" + }, + "isApprovalActive": { + "type": "boolean" + } + } + }, + "request.UserLevelsCreateRequest": { + "type": "object", + "required": [ + "aliasName", + "levelNumber", + "name" + ], + "properties": { + "aliasName": { + "type": "string" + }, + "group": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "isApprovalActive": { + "type": "boolean" + }, + "levelNumber": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parentLevelId": { + "type": "integer" + }, + "provinceId": { + "type": "integer" + } + } + }, + "request.UserLevelsUpdateRequest": { + "type": "object", + "required": [ + "aliasName", + "levelNumber", + "name" + ], + "properties": { + "aliasName": { + "type": "string" + }, + "group": { + "type": "string" + }, + "isApprovalActive": { + "type": "boolean" + }, + "levelNumber": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "parentLevelId": { + "type": "integer" + }, + "provinceId": { + "type": "integer" + } + } + }, + "request.UserLogin": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "refreshToken": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "request.UserOtpRequest": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "request.UserOtpValidation": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "otpCode": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "request.UserResetPassword": { + "type": "object", + "properties": { + "codeRequest": { + "type": "string" + }, + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + }, + "userId": { + "type": "string" + } + } + }, + "request.UserRoleAccessesCreateRequest": { + "type": "object", + "required": [ + "isAdminEnabled", + "isApprovalEnabled", + "isDeleteEnabled", + "isInsertEnabled", + "isUpdateEnabled", + "isViewEnabled", + "menuId" + ], + "properties": { + "isAdminEnabled": { + "type": "boolean" + }, + "isApprovalEnabled": { + "type": "boolean" + }, + "isDeleteEnabled": { + "type": "boolean" + }, + "isInsertEnabled": { + "type": "boolean" + }, + "isUpdateEnabled": { + "type": "boolean" + }, + "isViewEnabled": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + } + } + }, + "request.UserRoleAccessesUpdateRequest": { + "type": "object", + "required": [ + "id", + "is_admin_enabled", + "is_approval_enabled", + "is_delete_enabled", + "is_insert_enabled", + "is_update_enabled", + "is_view_enabled", + "menu_id", + "user_role_id" + ], + "properties": { + "id": { + "type": "integer" + }, + "is_admin_enabled": { + "type": "boolean" + }, + "is_approval_enabled": { + "type": "boolean" + }, + "is_delete_enabled": { + "type": "boolean" + }, + "is_insert_enabled": { + "type": "boolean" + }, + "is_update_enabled": { + "type": "boolean" + }, + "is_view_enabled": { + "type": "boolean" + }, + "menu_id": { + "type": "integer" + }, + "user_role_id": { + "type": "integer" + } + } + }, + "request.UserRolesCreateRequest": { + "type": "object", + "required": [ + "code", + "description", + "name", + "statusId", + "userLevelIds", + "userRoleAccess" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "userLevelIds": { + "type": "array", + "items": { + "type": "integer" + } + }, + "userRoleAccess": { + "type": "array", + "items": { + "$ref": "#/definitions/request.UserRoleAccessesCreateRequest" + } + } + } + }, + "request.UserRolesUpdateRequest": { + "type": "object", + "required": [ + "code", + "description", + "level_number", + "name", + "status_id", + "userLevelIds" + ], + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + }, + "level_number": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "status_id": { + "type": "integer" + }, + "userLevelIds": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "request.UserSavePassword": { + "type": "object", + "properties": { + "confirmPassword": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "request.UsersCreateRequest": { + "type": "object", + "required": [ + "email", + "fullname", + "password", + "userLevelId", + "userRoleId", + "username" + ], + "properties": { + "address": { + "type": "string" + }, + "dateOfBirth": { + "type": "string" + }, + "degree": { + "type": "string" + }, + "email": { + "type": "string" + }, + "fullname": { + "type": "string" + }, + "genderType": { + "type": "string" + }, + "identityGroup": { + "type": "string" + }, + "identityGroupNumber": { + "type": "string" + }, + "identityNumber": { + "type": "string" + }, + "identityType": { + "type": "string" + }, + "lastEducation": { + "type": "string" + }, + "lastJobTitle": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phoneNumber": { + "type": "string" + }, + "userLevelId": { + "type": "integer" + }, + "userRoleId": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "whatsappNumber": { + "type": "string" + }, + "workType": { + "type": "string" + } + } + }, + "request.UsersUpdateRequest": { + "type": "object", + "required": [ + "email", + "fullname", + "userLevelId", + "userRoleId", + "username" + ], + "properties": { + "address": { + "type": "string" + }, + "dateOfBirth": { + "type": "string" + }, + "degree": { + "type": "string" + }, + "email": { + "type": "string" + }, + "fullname": { + "type": "string" + }, + "genderType": { + "type": "string" + }, + "identityGroup": { + "type": "string" + }, + "identityGroupNumber": { + "type": "string" + }, + "identityNumber": { + "type": "string" + }, + "identityType": { + "type": "string" + }, + "lastEducation": { + "type": "string" + }, + "lastJobTitle": { + "type": "string" + }, + "phoneNumber": { + "type": "string" + }, + "statusId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + }, + "userRoleId": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "whatsappNumber": { + "type": "string" + }, + "workType": { + "type": "string" + } + } + }, + "request.WorkHistoryCreateRequest": { + "type": "object", + "required": [ + "companyName", + "jobTitle", + "startDate" + ], + "properties": { + "companyName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "endDate": { + "type": "string" + }, + "jobTitle": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "startDate": { + "type": "string" + } + } + }, + "request.WorkHistoryUpdateRequest": { + "type": "object", + "required": [ + "companyName", + "jobTitle", + "startDate" + ], + "properties": { + "companyName": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "endDate": { + "type": "string" + }, + "jobTitle": { + "type": "string", + "maxLength": 255, + "minLength": 2 + }, + "startDate": { + "type": "string" + } + } + }, + "response.BadRequestError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 400 + }, + "message": { + "type": "string", + "example": "bad request" + }, + "success": { + "type": "boolean", + "example": false + } + } + }, + "response.InternalServerError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 500 + }, + "message": { + "type": "string", + "example": "internal server error" + }, + "success": { + "type": "boolean", + "example": false + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "data": {}, + "messages": { + "type": "array", + "items": {} + }, + "meta": {}, + "success": { + "type": "boolean", + "example": true + } + } + }, + "response.UnauthorizedError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 401 + }, + "message": { + "type": "string", + "example": "unauthorized access" + }, + "success": { + "type": "boolean", + "example": false + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml new file mode 100644 index 0000000..55808d4 --- /dev/null +++ b/docs/swagger/swagger.yaml @@ -0,0 +1,8152 @@ +definitions: + paginator.Pagination: + properties: + count: + type: integer + limit: + type: integer + nextPage: + type: integer + page: + type: integer + previousPage: + type: integer + sort: + type: string + sortBy: + type: string + totalPage: + type: integer + type: object + request.ActivityLogsCreateRequest: + properties: + activityTypeId: + type: integer + articleId: + type: integer + url: + type: string + userId: + type: integer + visitorIp: + type: string + required: + - activityTypeId + - url + type: object + request.ActivityLogsUpdateRequest: + properties: + activityTypeId: + type: integer + articleId: + type: integer + id: + type: integer + url: + type: string + userId: + type: integer + required: + - activityTypeId + - id + - url + type: object + request.AdvertisementCreateRequest: + properties: + description: + type: string + placement: + type: string + redirectLink: + type: string + title: + type: string + required: + - description + - placement + - redirectLink + - title + type: object + request.AdvertisementUpdateRequest: + properties: + description: + type: string + id: + type: integer + placement: + type: string + redirectLink: + type: string + title: + type: string + required: + - description + - id + - placement + - redirectLink + - title + type: object + request.ArticleApprovalsCreateRequest: + properties: + articleId: + type: integer + message: + type: string + statusId: + type: integer + required: + - articleId + - message + - statusId + type: object + request.ArticleApprovalsUpdateRequest: + properties: + articleId: + type: integer + id: + type: integer + message: + type: string + statusId: + type: integer + required: + - articleId + - id + - message + - statusId + type: object + request.ArticleCategoriesCreateRequest: + properties: + createdById: + type: integer + description: + type: string + oldCategoryId: + type: integer + parentId: + type: integer + slug: + type: string + statusId: + type: integer + tags: + type: string + title: + type: string + required: + - description + - statusId + - title + type: object + request.ArticleCategoriesUpdateRequest: + properties: + createdById: + type: integer + description: + type: string + id: + type: integer + isPublish: + type: boolean + parentId: + type: integer + publishedAt: + type: string + slug: + type: string + statusId: + type: integer + tags: + type: string + title: + type: string + required: + - description + - id + - statusId + - title + type: object + request.ArticleCommentsApprovalRequest: + properties: + id: + type: integer + statusId: + type: integer + required: + - id + - statusId + type: object + request.ArticleCommentsCreateRequest: + properties: + articleId: + type: integer + isPublic: + type: boolean + message: + type: string + parentId: + type: integer + required: + - articleId + - message + type: object + request.ArticleCommentsUpdateRequest: + properties: + articleId: + type: integer + id: + type: integer + isPublic: + type: boolean + message: + type: string + parentId: + type: integer + required: + - articleId + - id + - message + type: object + request.ArticleFilesUpdateRequest: + properties: + articleId: + type: integer + fileAlt: + type: string + fileName: + type: string + filePath: + type: string + fileThumbnail: + type: string + fileUrl: + type: string + heightPixel: + type: string + id: + type: integer + isPublish: + type: boolean + publishedAt: + type: string + size: + type: string + statusId: + type: integer + widthPixel: + type: string + required: + - articleId + - id + - isPublish + - publishedAt + - statusId + type: object + request.ArticlesCreateRequest: + properties: + aiArticleId: + type: integer + categoryIds: + type: string + createdAt: + type: string + createdById: + type: integer + description: + type: string + htmlDescription: + type: string + isDraft: + type: boolean + isPublish: + type: boolean + oldId: + type: integer + slug: + type: string + tags: + type: string + title: + type: string + typeId: + type: integer + required: + - categoryIds + - description + - htmlDescription + - slug + - tags + - title + - typeId + type: object + request.ArticlesUpdateRequest: + properties: + aiArticleId: + type: integer + categoryIds: + type: string + createdAt: + type: string + createdById: + type: integer + description: + type: string + htmlDescription: + type: string + isDraft: + type: boolean + isPublish: + type: boolean + slug: + type: string + statusId: + type: integer + tags: + type: string + title: + type: string + typeId: + type: integer + required: + - categoryIds + - description + - htmlDescription + - slug + - tags + - title + - typeId + type: object + request.CitiesCreateRequest: + properties: + city_name: + type: string + prov_id: + type: integer + required: + - city_name + - prov_id + type: object + request.CitiesUpdateRequest: + properties: + city_name: + type: string + id: + type: integer + prov_id: + type: integer + required: + - city_name + - id + - prov_id + type: object + request.CustomStaticPagesCreateRequest: + properties: + description: + type: string + htmlBody: + type: string + slug: + type: string + title: + type: string + required: + - htmlBody + - slug + - title + type: object + request.CustomStaticPagesUpdateRequest: + properties: + description: + type: string + htmlBody: + type: string + id: + type: integer + slug: + type: string + title: + type: string + updated_at: + type: string + required: + - htmlBody + - id + - slug + - title + type: object + request.EducationHistoryCreateRequest: + properties: + certificateImage: + type: string + educationLevel: + maxLength: 100 + minLength: 2 + type: string + graduationYear: + maximum: 2030 + minimum: 1950 + type: integer + major: + maxLength: 255 + minLength: 2 + type: string + schoolName: + maxLength: 255 + minLength: 2 + type: string + required: + - educationLevel + - graduationYear + - major + - schoolName + type: object + request.EducationHistoryUpdateRequest: + properties: + certificateImage: + type: string + educationLevel: + maxLength: 100 + minLength: 2 + type: string + graduationYear: + maximum: 2030 + minimum: 1950 + type: integer + major: + maxLength: 255 + minLength: 2 + type: string + schoolName: + maxLength: 255 + minLength: 2 + type: string + required: + - educationLevel + - graduationYear + - major + - schoolName + type: object + request.FeedbacksCreateRequest: + properties: + commentFromEmail: + type: string + commentFromName: + type: string + message: + type: string + required: + - commentFromEmail + - commentFromName + - message + type: object + request.FeedbacksUpdateRequest: + properties: + commentFromEmail: + type: string + commentFromName: + type: string + id: + type: integer + message: + type: string + required: + - commentFromEmail + - commentFromName + - id + - message + type: object + request.MagazinesCreateRequest: + properties: + createdById: + type: integer + description: + type: string + isPublish: + type: boolean + pageUrl: + type: string + publishedAt: + type: string + statusId: + type: integer + thumbnailPath: + type: string + thumbnailUrl: + type: string + title: + type: string + required: + - description + - statusId + - title + type: object + request.MagazinesUpdateRequest: + properties: + createdById: + type: integer + description: + type: string + id: + type: integer + isPublish: + type: boolean + pageUrl: + type: string + publishedAt: + type: string + statusId: + type: integer + thumbnailPath: + type: string + thumbnailUrl: + type: string + title: + type: string + required: + - description + - id + - statusId + - title + type: object + request.MasterMenusCreateRequest: + properties: + description: + type: string + group: + type: string + icon: + type: string + moduleId: + type: integer + name: + type: string + parentMenuId: + type: integer + statusId: + type: integer + required: + - description + - group + - moduleId + - name + - statusId + type: object + request.MasterModulesCreateRequest: + properties: + description: + type: string + name: + type: string + pathUrl: + type: string + statusId: + type: integer + required: + - description + - name + - pathUrl + - statusId + type: object + request.MasterModulesUpdateRequest: + properties: + description: + type: string + id: + type: integer + name: + type: string + pathUrl: + type: string + statusId: + type: integer + required: + - description + - id + - name + - pathUrl + - statusId + type: object + request.ResearchJournalsCreateRequest: + properties: + journalTitle: + maxLength: 500 + minLength: 2 + type: string + journalUrl: + type: string + publishedDate: + type: string + publisher: + maxLength: 255 + minLength: 2 + type: string + required: + - journalTitle + - journalUrl + - publisher + type: object + request.ResearchJournalsUpdateRequest: + properties: + journalTitle: + maxLength: 500 + minLength: 2 + type: string + journalUrl: + type: string + publishedDate: + type: string + publisher: + maxLength: 255 + minLength: 2 + type: string + required: + - journalTitle + - journalUrl + - publisher + type: object + request.SubscriptionCreateRequest: + properties: + email: + type: string + required: + - email + type: object + request.SubscriptionUpdateRequest: + properties: + email: + type: string + id: + type: integer + required: + - email + - id + type: object + request.UserEmailValidationRequest: + properties: + newEmail: + type: string + oldEmail: + type: string + password: + type: string + username: + type: string + type: object + request.UserForgotPassword: + properties: + username: + type: string + type: object + request.UserLevelsApprovalRequest: + properties: + ids: + type: string + isApprovalActive: + type: boolean + required: + - ids + - isApprovalActive + type: object + request.UserLevelsCreateRequest: + properties: + aliasName: + type: string + group: + type: string + isActive: + type: boolean + isApprovalActive: + type: boolean + levelNumber: + type: integer + name: + type: string + parentLevelId: + type: integer + provinceId: + type: integer + required: + - aliasName + - levelNumber + - name + type: object + request.UserLevelsUpdateRequest: + properties: + aliasName: + type: string + group: + type: string + isApprovalActive: + type: boolean + levelNumber: + type: integer + name: + type: string + parentLevelId: + type: integer + provinceId: + type: integer + required: + - aliasName + - levelNumber + - name + type: object + request.UserLogin: + properties: + password: + type: string + refreshToken: + type: string + username: + type: string + type: object + request.UserOtpRequest: + properties: + email: + type: string + name: + type: string + required: + - email + type: object + request.UserOtpValidation: + properties: + email: + type: string + otpCode: + type: string + username: + type: string + type: object + request.UserResetPassword: + properties: + codeRequest: + type: string + confirmPassword: + type: string + password: + type: string + userId: + type: string + type: object + request.UserRoleAccessesCreateRequest: + properties: + isAdminEnabled: + type: boolean + isApprovalEnabled: + type: boolean + isDeleteEnabled: + type: boolean + isInsertEnabled: + type: boolean + isUpdateEnabled: + type: boolean + isViewEnabled: + type: boolean + menuId: + type: integer + required: + - isAdminEnabled + - isApprovalEnabled + - isDeleteEnabled + - isInsertEnabled + - isUpdateEnabled + - isViewEnabled + - menuId + type: object + request.UserRoleAccessesUpdateRequest: + properties: + id: + type: integer + is_admin_enabled: + type: boolean + is_approval_enabled: + type: boolean + is_delete_enabled: + type: boolean + is_insert_enabled: + type: boolean + is_update_enabled: + type: boolean + is_view_enabled: + type: boolean + menu_id: + type: integer + user_role_id: + type: integer + required: + - id + - is_admin_enabled + - is_approval_enabled + - is_delete_enabled + - is_insert_enabled + - is_update_enabled + - is_view_enabled + - menu_id + - user_role_id + type: object + request.UserRolesCreateRequest: + properties: + code: + type: string + description: + type: string + name: + type: string + statusId: + type: integer + userLevelIds: + items: + type: integer + type: array + userRoleAccess: + items: + $ref: '#/definitions/request.UserRoleAccessesCreateRequest' + type: array + required: + - code + - description + - name + - statusId + - userLevelIds + - userRoleAccess + type: object + request.UserRolesUpdateRequest: + properties: + code: + type: string + description: + type: string + level_number: + type: integer + name: + type: string + status_id: + type: integer + userLevelIds: + items: + type: integer + type: array + required: + - code + - description + - level_number + - name + - status_id + - userLevelIds + type: object + request.UserSavePassword: + properties: + confirmPassword: + type: string + password: + type: string + type: object + request.UsersCreateRequest: + properties: + address: + type: string + dateOfBirth: + type: string + degree: + type: string + email: + type: string + fullname: + type: string + genderType: + type: string + identityGroup: + type: string + identityGroupNumber: + type: string + identityNumber: + type: string + identityType: + type: string + lastEducation: + type: string + lastJobTitle: + type: string + password: + type: string + phoneNumber: + type: string + userLevelId: + type: integer + userRoleId: + type: integer + username: + type: string + whatsappNumber: + type: string + workType: + type: string + required: + - email + - fullname + - password + - userLevelId + - userRoleId + - username + type: object + request.UsersUpdateRequest: + properties: + address: + type: string + dateOfBirth: + type: string + degree: + type: string + email: + type: string + fullname: + type: string + genderType: + type: string + identityGroup: + type: string + identityGroupNumber: + type: string + identityNumber: + type: string + identityType: + type: string + lastEducation: + type: string + lastJobTitle: + type: string + phoneNumber: + type: string + statusId: + type: integer + userLevelId: + type: integer + userRoleId: + type: integer + username: + type: string + whatsappNumber: + type: string + workType: + type: string + required: + - email + - fullname + - userLevelId + - userRoleId + - username + type: object + request.WorkHistoryCreateRequest: + properties: + companyName: + maxLength: 255 + minLength: 2 + type: string + endDate: + type: string + jobTitle: + maxLength: 255 + minLength: 2 + type: string + startDate: + type: string + required: + - companyName + - jobTitle + - startDate + type: object + request.WorkHistoryUpdateRequest: + properties: + companyName: + maxLength: 255 + minLength: 2 + type: string + endDate: + type: string + jobTitle: + maxLength: 255 + minLength: 2 + type: string + startDate: + type: string + required: + - companyName + - jobTitle + - startDate + type: object + response.BadRequestError: + properties: + code: + example: 400 + type: integer + message: + example: bad request + type: string + success: + example: false + type: boolean + type: object + response.InternalServerError: + properties: + code: + example: 500 + type: integer + message: + example: internal server error + type: string + success: + example: false + type: boolean + type: object + response.Response: + properties: + code: + example: 200 + type: integer + data: {} + messages: + items: {} + type: array + meta: {} + success: + example: true + type: boolean + type: object + response.UnauthorizedError: + properties: + code: + example: 401 + type: integer + message: + example: unauthorized access + type: string + success: + example: false + type: boolean + type: object +info: + contact: {} +paths: + /activity-logs: + get: + description: API for getting all ActivityLogs + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: activityTypeId + type: integer + - in: query + name: articleId + type: integer + - in: query + name: url + type: string + - in: query + name: userId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all ActivityLogs + tags: + - ActivityLogs + post: + description: API for create ActivityLogs + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ActivityLogsCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create ActivityLogs + tags: + - ActivityLogs + /activity-logs/{id}: + delete: + description: API for delete ActivityLogs + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ActivityLogs ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete ActivityLogs + tags: + - ActivityLogs + put: + description: API for update ActivityLogs + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ActivityLogsUpdateRequest' + - description: ActivityLogs ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update ActivityLogs + tags: + - ActivityLogs + /activity-logs/detail/{id}: + get: + description: API for getting one ActivityLogs + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ActivityLogs ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ActivityLogs + tags: + - ActivityLogs + /activity-logs/statistics: + get: + description: API for get activity stats ActivityLogs + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get activity stats ActivityLogs + tags: + - ActivityLogs + /advertisement: + get: + description: API for getting all Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: description + type: string + - in: query + name: isPublish + type: boolean + - in: query + name: placement + type: string + - in: query + name: redirectLink + type: string + - in: query + name: statusId + type: integer + - in: query + name: title + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Advertisement + tags: + - Advertisement + post: + description: API for create Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.AdvertisementCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Advertisement + tags: + - Advertisement + /advertisement/{id}: + delete: + description: API for delete Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Advertisement ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete Advertisement + tags: + - Advertisement + get: + description: API for getting one Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Advertisement ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Advertisement + tags: + - Advertisement + put: + description: API for update Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.AdvertisementUpdateRequest' + - description: Advertisement ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update Advertisement + tags: + - Advertisement + /advertisement/publish/{id}: + put: + description: API for Update Publish Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Advertisement ID + in: path + name: id + required: true + type: integer + - description: Advertisement Publish Status + in: query + name: isPublish + required: true + type: boolean + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Publish Advertisement + tags: + - Advertisement + /advertisement/upload/{id}: + post: + description: API for Upload File Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Upload file + in: formData + name: file + required: true + type: file + - description: Advertisement ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Upload Advertisement + tags: + - Advertisement + /advertisement/viewer/{filename}: + get: + description: API for Viewer Advertisement + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Content File Name + in: path + name: filename + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Viewer Advertisement + tags: + - Advertisement + /article-approvals: + get: + description: API for getting all ArticleApprovals + parameters: + - in: query + name: approvalAtLevel + type: integer + - in: query + name: approvalBy + type: integer + - in: query + name: articleId + type: integer + - in: query + name: message + type: string + - in: query + name: statusId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all ArticleApprovals + tags: + - ArticleApprovals + post: + description: API for create ArticleApprovals + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleApprovalsCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create ArticleApprovals + tags: + - ArticleApprovals + /article-approvals/{id}: + delete: + description: API for delete ArticleApprovals + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ArticleApprovals ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete ArticleApprovals + tags: + - ArticleApprovals + get: + description: API for getting one ArticleApprovals + parameters: + - description: ArticleApprovals ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ArticleApprovals + tags: + - ArticleApprovals + put: + description: API for update ArticleApprovals + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleApprovalsUpdateRequest' + - description: ArticleApprovals ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update ArticleApprovals + tags: + - ArticleApprovals + /article-categories: + get: + description: API for getting all ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - in: query + name: UserLevelId + type: integer + - in: query + name: UserLevelNumber + type: integer + - in: query + name: description + type: string + - in: query + name: isPublish + type: boolean + - in: query + name: parentId + type: integer + - in: query + name: statusId + type: integer + - in: query + name: title + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all ArticleCategories + tags: + - Article Categories + post: + description: API for create ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleCategoriesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create ArticleCategories + tags: + - Article Categories + /article-categories/{id}: + delete: + description: API for delete ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ArticleCategories ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete ArticleCategories + tags: + - Article Categories + get: + description: API for getting one ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ArticleCategories ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ArticleCategories + tags: + - Article Categories + put: + description: API for update ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleCategoriesUpdateRequest' + - description: ArticleCategories ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update ArticleCategories + tags: + - Article Categories + /article-categories/old/{id}: + get: + description: API for getting one ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ArticleCategories Old ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ArticleCategories + tags: + - Article Categories + /article-categories/slug/{slug}: + get: + description: API for getting one ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ArticleCategories Slug + in: path + name: slug + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ArticleCategories + tags: + - Article Categories + /article-categories/thumbnail/{id}: + post: + description: API for Upload ArticleCategories Thumbnail + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Upload thumbnail + in: formData + name: files + required: true + type: file + - description: ArticleCategories ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Upload ArticleCategories Thumbnail + tags: + - Article Categories + /article-categories/thumbnail/viewer/{id}: + get: + description: API for View Thumbnail of ArticleCategories + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ArticleCategories ID + in: path + name: id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Viewer ArticleCategories + tags: + - Article Categories + /article-category-details: + get: + description: API for getting all ArticleCategoryDetails + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get all ArticleCategoryDetails + tags: + - Untags + post: + description: API for create ArticleCategoryDetails + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Create ArticleCategoryDetails + tags: + - Untags + /article-category-details/{id}: + delete: + description: API for delete ArticleCategoryDetails + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ArticleCategoryDetails ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: delete ArticleCategoryDetails + tags: + - Untags + get: + description: API for getting one ArticleCategoryDetails + parameters: + - description: ArticleCategoryDetails ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get one ArticleCategoryDetails + tags: + - Untags + put: + description: API for update ArticleCategoryDetails + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ArticleCategoryDetails ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: update ArticleCategoryDetails + tags: + - Untags + /article-comments: + get: + description: API for getting all ArticleComments + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: articleId + type: integer + - in: query + name: commentFrom + type: integer + - in: query + name: isPublic + type: boolean + - in: query + name: message + type: string + - in: query + name: parentId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all ArticleComments + tags: + - ArticleComments + post: + description: API for create ArticleComments + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleCommentsCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create ArticleComments + tags: + - ArticleComments + /article-comments/{id}: + delete: + description: API for delete ArticleComments + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ArticleComments ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete ArticleComments + tags: + - ArticleComments + get: + description: API for getting one ArticleComments + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ArticleComments ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ArticleComments + tags: + - ArticleComments + put: + description: API for update ArticleComments + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleCommentsUpdateRequest' + - description: ArticleComments ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update ArticleComments + tags: + - ArticleComments + /article-comments/approval: + post: + description: API for Approval ArticleComments + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleCommentsApprovalRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Approval ArticleComments + tags: + - ArticleComments + /article-files: + get: + description: API for getting all ArticleFiles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: articleId + type: integer + - in: query + name: fileName + type: string + - in: query + name: isPublish + type: boolean + - in: query + name: statusId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all ArticleFiles + tags: + - Article Files + /article-files/{articleId}: + post: + description: API for create ArticleFiles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Upload file + in: formData + name: files + required: true + type: file + - description: Article ID + in: path + name: articleId + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Upload ArticleFiles + tags: + - Article Files + /article-files/{id}: + delete: + description: API for delete ArticleFiles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: ArticleFiles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete ArticleFiles + tags: + - Article Files + get: + description: API for getting one ArticleFiles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: ArticleFiles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one ArticleFiles + tags: + - Article Files + put: + description: API for update ArticleFiles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticleFilesUpdateRequest' + - description: ArticleFiles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update ArticleFiles + tags: + - Article Files + /article-files/upload-status/{uploadId}: + get: + description: API for GetUploadStatus ArticleFiles + parameters: + - description: Upload ID of ArticleFiles + in: path + name: uploadId + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: GetUploadStatus ArticleFiles + tags: + - Article Files + /article-files/viewer/{filename}: + get: + description: API for Viewer ArticleFiles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Article File Name + in: path + name: filename + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Viewer ArticleFiles + tags: + - Article Files + /articles: + get: + description: API for getting all Articles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + required: true + type: string + - in: query + name: category + type: string + - in: query + name: categoryId + type: integer + - in: query + name: createdById + type: integer + - in: query + name: description + type: string + - in: query + name: isBanner + type: boolean + - in: query + name: isDraft + type: boolean + - in: query + name: isPublish + type: boolean + - in: query + name: statusId + type: integer + - in: query + name: tags + type: string + - in: query + name: title + type: string + - in: query + name: typeId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Articles + tags: + - Articles + post: + description: API for create Articles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticlesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Articles + tags: + - Articles + /articles/{id}: + delete: + description: API for delete Articles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Articles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete Articles + tags: + - Articles + get: + description: API for getting one Articles + parameters: + - description: Articles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Articles + tags: + - Articles + put: + description: API for update Articles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ArticlesUpdateRequest' + - description: Articles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Articles + tags: + - Articles + /articles/banner/{id}: + put: + description: API for Update Banner Articles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Articles ID + in: path + name: id + required: true + type: integer + - description: Articles Banner Status + in: query + name: isBanner + required: true + type: boolean + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Banner Articles + tags: + - Articles + /articles/old-id/{id}: + get: + description: API for getting one Articles + parameters: + - description: Articles Old ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Articles + tags: + - Articles + /articles/publish-scheduling: + post: + description: API for Publish Schedule of Article + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: article id + in: query + name: id + type: integer + - description: publish date + in: query + name: date + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: PublishScheduling Articles + tags: + - Articles + /articles/statistic/monthly: + get: + description: API for ArticleMonthlyStats of Article + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: year + in: query + name: year + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ArticleMonthlyStats Articles + tags: + - Articles + /articles/statistic/summary: + get: + description: API for Summary Stats of Article + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: SummaryStats Articles + tags: + - Articles + /articles/statistic/user-levels: + get: + description: API for ArticlePerUserLevelStats of Article + parameters: + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: start date + in: query + name: startDate + type: string + - description: start date + in: query + name: endDate + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ArticlePerUserLevelStats Articles + tags: + - Articles + /articles/thumbnail/{id}: + post: + description: API for Save Thumbnail of Articles + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Upload thumbnail + in: formData + name: files + required: true + type: file + - description: Articles ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Save Thumbnail Articles + tags: + - Articles + /articles/thumbnail/viewer/{thumbnailName}: + get: + description: API for View Thumbnail of Article + parameters: + - description: Articles Thumbnail Name + in: path + name: thumbnailName + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Viewer Articles Thumbnail + tags: + - Articles + /cities: + get: + description: API for getting all Cities + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get all Cities + tags: + - Untags + post: + description: API for create Cities + parameters: + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.CitiesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Create Cities + tags: + - Untags + /cities/{id}: + delete: + description: API for delete Cities + parameters: + - description: Cities ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Delete Cities + tags: + - Untags + get: + description: API for getting one Cities + parameters: + - description: Cities ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get one Cities + tags: + - Untags + put: + consumes: + - application/json + description: API for update Cities + parameters: + - description: Cities ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.CitiesUpdateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Update Cities + tags: + - Untags + /custom-static-pages: + get: + description: API for getting all CustomStaticPages + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: description + type: string + - in: query + name: htmlBody + type: string + - in: query + name: slug + type: string + - in: query + name: title + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all CustomStaticPages + tags: + - CustomStaticPages + post: + description: API for create CustomStaticPages + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.CustomStaticPagesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create CustomStaticPages + tags: + - CustomStaticPages + /custom-static-pages/{id}: + delete: + description: API for delete CustomStaticPages + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: CustomStaticPages ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete CustomStaticPages + tags: + - CustomStaticPages + get: + description: API for getting one CustomStaticPages + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: CustomStaticPages ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one CustomStaticPages + tags: + - CustomStaticPages + put: + description: API for update CustomStaticPages + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.CustomStaticPagesUpdateRequest' + - description: CustomStaticPages ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update CustomStaticPages + tags: + - CustomStaticPages + /custom-static-pages/slug/{slug}: + get: + description: API for getting one CustomStaticPages + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: CustomStaticPages Slug + in: path + name: slug + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one CustomStaticPages + tags: + - CustomStaticPages + /districts: + get: + description: API for getting all Districts + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get all Districts + tags: + - Untags + post: + description: API for create Districts + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Create Districts + tags: + - Untags + /districts/{id}: + delete: + description: API for delete Districts + parameters: + - description: Districts ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Delete Districts + tags: + - Untags + get: + description: API for getting one Districts + parameters: + - description: Districts ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get one Districts + tags: + - Untags + put: + description: API for update Districts + parameters: + - description: Districts ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Update Districts + tags: + - Untags + /education-history: + get: + description: API for getting all Education History for authenticated user + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: educationLevel + type: string + - in: query + name: graduationYear + type: integer + - in: query + name: major + type: string + - in: query + name: schoolName + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Education History + tags: + - Education History + post: + description: API for create Education History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.EducationHistoryCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Education History + tags: + - Education History + /education-history/{id}: + delete: + description: API for delete Education History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Education History ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete Education History + tags: + - Education History + get: + description: API for getting one Education History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Education History ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Education History + tags: + - Education History + put: + description: API for update Education History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Education History ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.EducationHistoryUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Education History + tags: + - Education History + /education-history/{id}/certificate: + post: + description: API for upload certificate image for Education History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Education History ID + in: path + name: id + required: true + type: integer + - description: Certificate image file + in: formData + name: certificate + required: true + type: file + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Upload Certificate for Education History + tags: + - Education History + /feedbacks: + get: + description: API for getting all Feedbacks + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: commentFromEmail + type: string + - in: query + name: commentFromName + type: string + - in: query + name: endDate + type: string + - in: query + name: message + type: string + - in: query + name: startDate + type: string + - in: query + name: statusId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Feedbacks + tags: + - Feedbacks + post: + description: API for create Feedbacks + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.FeedbacksCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Feedbacks + tags: + - Feedbacks + /feedbacks/{id}: + delete: + description: API for delete Feedbacks + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Feedbacks ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete Feedbacks + tags: + - Feedbacks + get: + description: API for getting one Feedbacks + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Feedbacks ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Feedbacks + tags: + - Feedbacks + put: + description: API for update Feedbacks + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.FeedbacksUpdateRequest' + - description: Feedbacks ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update Feedbacks + tags: + - Feedbacks + /feedbacks/statistic/monthly: + get: + description: API for FeedbackMonthlyStats of Feedbacks + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: year + in: query + name: year + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: FeedbackMonthlyStats Feedbacks + tags: + - Feedbacks + /magazine-files: + get: + description: API for getting all MagazineFiles + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all MagazineFiles + tags: + - Magazine Files + /magazine-files/{id}: + delete: + description: API for delete MagazineFiles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: MagazineFiles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete MagazineFiles + tags: + - Magazine Files + get: + description: API for getting one MagazineFiles + parameters: + - description: MagazineFiles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one MagazineFiles + tags: + - Magazine Files + put: + description: API for update MagazineFiles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: MagazineFiles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update MagazineFiles + tags: + - Magazine Files + /magazine-files/{magazineId}: + post: + description: API for create MagazineFiles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Upload file + in: formData + name: files + required: true + type: file + - description: Magazine file title + in: formData + name: title + required: true + type: string + - description: Magazine file description + in: formData + name: description + required: true + type: string + - description: Magazine ID + in: path + name: magazineId + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create MagazineFiles + tags: + - Magazine Files + /magazine-files/viewer/{filename}: + get: + description: API for create MagazineFiles + parameters: + - description: Magazine File Name + in: path + name: filename + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create MagazineFiles + tags: + - Magazine Files + /magazines: + get: + description: API for getting all Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: createdById + type: integer + - in: query + name: description + type: string + - in: query + name: isPublish + type: boolean + - in: query + name: pageUrl + type: string + - in: query + name: statusId + type: integer + - in: query + name: thumbnailPath + type: string + - in: query + name: thumbnailUrl + type: string + - in: query + name: title + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Magazines + tags: + - Magazines + post: + description: API for create Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MagazinesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Magazines + tags: + - Magazines + /magazines/{id}: + delete: + description: API for delete Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Magazines ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete Magazines + tags: + - Magazines + get: + description: API for getting one Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Magazines ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Magazines + tags: + - Magazines + put: + description: API for update Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Magazines ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MagazinesUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Magazines + tags: + - Magazines + /magazines/thumbnail/{id}: + post: + description: API for Save Thumbnail of Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Magazine ID + in: path + name: id + required: true + type: integer + - description: Upload thumbnail + in: formData + name: files + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Save Thumbnail Magazines + tags: + - Magazines + /magazines/thumbnail/viewer/{thumbnailName}: + get: + description: API for View Thumbnail of Magazines + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Magazines Thumbnail Name + in: path + name: thumbnailName + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Viewer Magazines Thumbnail + tags: + - Magazines + /master-menus: + get: + description: API for getting all MasterMenus + parameters: + - in: query + name: description + type: string + - in: query + name: moduleId + type: integer + - in: query + name: name + type: string + - in: query + name: parentMenuId + type: integer + - in: query + name: statusId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all MasterMenus + tags: + - MasterMenus + post: + description: API for create MasterMenus + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MasterMenusCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create MasterMenus + tags: + - MasterMenus + /master-menus/{id}: + delete: + description: API for delete MasterMenus + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: MasterMenus ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete MasterMenus + tags: + - MasterMenus + get: + description: API for getting one MasterMenus + parameters: + - description: MasterMenus ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one MasterMenus + tags: + - MasterMenus + put: + description: API for update MasterMenus + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: MasterMenus ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Update MasterMenus + tags: + - MasterMenus + /master-modules: + get: + description: API for getting all MasterModules + parameters: + - in: query + name: description + type: string + - in: query + name: name + type: string + - in: query + name: statusId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all MasterModules + tags: + - MasterModules + post: + description: API for create MasterModules + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MasterModulesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create MasterModules + tags: + - MasterModules + /master-modules/{id}: + delete: + description: API for delete MasterModules + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: MasterModules ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete MasterModules + tags: + - MasterModules + get: + description: API for getting one MasterModules + parameters: + - description: MasterModules ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one MasterModules + tags: + - MasterModules + put: + description: API for update MasterModules + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: MasterModules ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MasterModulesUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update MasterModules + tags: + - MasterModules + /master-statuses: + get: + description: API for getting all MasterStatuses + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get all MasterStatuses + tags: + - Untags + post: + description: API for create MasterStatuses + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Create MasterStatuses + tags: + - Untags + /master-statuses/{id}: + delete: + description: API for delete MasterStatuses + parameters: + - description: MasterStatuses ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Delete MasterStatuses + tags: + - Untags + get: + description: API for getting one MasterStatuses + parameters: + - description: MasterStatuses ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get one MasterStatuses + tags: + - Untags + put: + description: API for update MasterStatuses + parameters: + - description: MasterStatuses ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Update MasterStatuses + tags: + - Untags + /provinces: + get: + description: API for getting all Provinces + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get all Provinces + tags: + - Untags + post: + description: API for create Provinces + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Create Provinces + tags: + - Untags + /provinces/{id}: + delete: + description: API for delete Provinces + parameters: + - description: Provinces ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Delete Provinces + tags: + - Untags + get: + description: API for getting one Provinces + parameters: + - description: Provinces ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get one Provinces + tags: + - Untags + put: + description: API for update Provinces + parameters: + - description: Provinces ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Update Provinces + tags: + - Untags + /research-journals: + get: + description: API for getting all Research Journals for authenticated user + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: journalTitle + type: string + - in: query + name: publishedYear + type: integer + - in: query + name: publisher + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Research Journals + tags: + - Research Journals + post: + description: API for create Research Journal + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ResearchJournalsCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Research Journal + tags: + - Research Journals + /research-journals/{id}: + delete: + description: API for delete Research Journal + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Research Journal ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete Research Journal + tags: + - Research Journals + get: + description: API for getting one Research Journal + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Research Journal ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Research Journal + tags: + - Research Journals + put: + description: API for update Research Journal + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Research Journal ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.ResearchJournalsUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Research Journal + tags: + - Research Journals + /subscription: + get: + description: API for getting all Subscription + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: email + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Subscription + tags: + - Subscription + post: + description: API for create Subscription + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.SubscriptionCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Subscription + tags: + - Subscription + /subscription/{id}: + delete: + description: API for delete Subscription + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Subscription ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete Subscription + tags: + - Subscription + get: + description: API for getting one Subscription + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Subscription ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Subscription + tags: + - Subscription + put: + description: API for update Subscription + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.SubscriptionUpdateRequest' + - description: Subscription ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update Subscription + tags: + - Subscription + /user-levels: + get: + description: API for getting all UserLevels + parameters: + - in: query + name: levelNumber + type: integer + - in: query + name: name + type: string + - in: query + name: parentLevelId + type: integer + - in: query + name: provinceId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all UserLevels + tags: + - UserLevels + post: + description: API for create UserLevels + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelsCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserLevels + tags: + - UserLevels + /user-levels/{id}: + delete: + description: API for delete UserLevels + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: UserLevels ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: delete UserLevels + tags: + - UserLevels + get: + description: API for getting one UserLevels + parameters: + - description: UserLevels ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one UserLevels + tags: + - UserLevels + put: + description: API for update UserLevels + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelsUpdateRequest' + - description: UserLevels ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update UserLevels + tags: + - UserLevels + /user-levels/alias/{alias}: + get: + description: API for getting one UserLevels + parameters: + - description: UserLevels Alias + in: path + name: alias + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one UserLevels + tags: + - UserLevels + /user-levels/enable-approval: + post: + description: API for Enable Approval of Article + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelsApprovalRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: EnableApproval Articles + tags: + - UserLevels + /user-role-accesses: + get: + description: API for getting all UserRoleAccesses + parameters: + - in: query + name: isActive + required: true + type: boolean + - in: query + name: menuId + required: true + type: integer + - in: query + name: userRoleId + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all UserRoleAccesses + tags: + - UserRoleAccesses + post: + description: API for create UserRoleAccesses + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserRoleAccessesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserRoleAccesses + tags: + - UserRoleAccesses + /user-role-accesses/{id}: + delete: + description: API for delete UserRoleAccesses + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: UserRoleAccesses ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete UserRoleAccesses + tags: + - UserRoleAccesses + get: + description: API for getting one UserRoleAccesses + parameters: + - description: UserRoleAccesses ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one UserRoleAccesses + tags: + - UserRoleAccesses + put: + description: API for update UserRoleAccesses + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserRoleAccessesUpdateRequest' + - description: UserRoleAccesses ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update UserRoleAccesses + tags: + - UserRoleAccesses + /user-role-level-details: + get: + description: API for getting all UserRoleLevelDetails + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get all UserRoleLevelDetails + tags: + - Task + post: + description: API for create UserRoleLevelDetails + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Create UserRoleLevelDetails + tags: + - Task + /user-role-level-details/{id}: + delete: + description: API for delete UserRoleLevelDetails + parameters: + - description: UserRoleLevelDetails ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: delete UserRoleLevelDetails + tags: + - Task + get: + description: API for getting one UserRoleLevelDetails + parameters: + - description: UserRoleLevelDetails ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: Get one UserRoleLevelDetails + tags: + - Task + put: + description: API for update UserRoleLevelDetails + parameters: + - description: UserRoleLevelDetails ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.Response' + "404": + description: Not Found + schema: + $ref: '#/definitions/response.Response' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + security: + - Bearer: [] + summary: update UserRoleLevelDetails + tags: + - Task + /user-roles: + get: + description: API for getting all UserRoles + parameters: + - in: query + name: code + type: string + - in: query + name: description + type: string + - in: query + name: name + type: string + - in: query + name: statusId + type: integer + - in: query + name: userLevelId + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all UserRoles + tags: + - UserRoles + post: + description: API for create UserRoles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserRolesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserRoles + tags: + - UserRoles + /user-roles/{id}: + delete: + description: API for delete UserRoles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: UserRoles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete UserRoles + tags: + - UserRoles + get: + description: API for getting one UserRoles + parameters: + - description: UserRoles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one UserRoles + tags: + - UserRoles + put: + description: API for update UserRoles + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserRolesUpdateRequest' + - description: UserRoles ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update UserRoles + tags: + - UserRoles + /users: + get: + description: API for getting all Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: email + type: string + - in: query + name: fullname + type: string + - in: query + name: genderType + type: string + - in: query + name: identityGroup + type: string + - in: query + name: identityGroupNumber + type: string + - in: query + name: identityNumber + type: string + - in: query + name: identityType + type: string + - in: query + name: phoneNumber + type: string + - in: query + name: statusId + type: integer + - in: query + name: userRoleId + type: integer + - in: query + name: username + type: string + - in: query + name: workType + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Users + tags: + - Users + post: + description: API for create Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UsersCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Users + tags: + - Users + /users/{id}: + delete: + description: API for delete Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Users ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: delete Users + tags: + - Users + put: + description: API for update Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Users ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UsersUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: update Users + tags: + - Users + /users/detail/{id}: + get: + description: API for getting one Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Users ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Users + tags: + - Users + /users/email-validation: + post: + description: API for Email Validation Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserEmailValidationRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: EmailValidation Users + tags: + - Users + /users/forgot-password: + post: + description: API for ForgotPassword Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserForgotPassword' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ForgotPassword Users + tags: + - Users + /users/info: + get: + description: API for ShowUserInfo + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ShowInfo Users + tags: + - Users + /users/login: + post: + description: API for Login Users + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLogin' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Login Users + tags: + - Users + /users/otp-request: + post: + description: API for OtpRequest Users + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserOtpRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: OtpRequest Users + tags: + - Users + /users/otp-validation: + post: + description: API for OtpValidation Users + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserOtpValidation' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: OtpValidation Users + tags: + - Users + /users/pareto-login: + post: + description: API for ParetoLogin Users + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLogin' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ParetoLogin Users + tags: + - Users + /users/reset-password: + post: + description: API for ResetPassword Users + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserResetPassword' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: ResetPassword Users + tags: + - Users + /users/save-password: + post: + description: API for SavePassword Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserSavePassword' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: SavePassword Users + tags: + - Users + /users/setup-email: + post: + description: API for Setup Email Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserEmailValidationRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: SetupEmail Users + tags: + - Users + /users/username/{username}: + get: + description: API for getting one Users + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Username + in: path + name: username + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Users + tags: + - Users + /work-history: + get: + description: API for getting all Work History for authenticated user + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - in: query + name: companyName + type: string + - description: filter for current job (EndDate is null) + in: query + name: isCurrent + type: boolean + - in: query + name: jobTitle + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all Work History + tags: + - Work History + post: + description: API for create Work History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - default: Bearer + description: Insert your access token + in: header + name: Authorization + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.WorkHistoryCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create Work History + tags: + - Work History + /work-history/{id}: + delete: + description: API for delete Work History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Work History ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete Work History + tags: + - Work History + get: + description: API for getting one Work History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Work History ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one Work History + tags: + - Work History + put: + description: API for update Work History + parameters: + - description: Insert the X-Client-Key + in: header + name: X-Client-Key + type: string + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + required: true + type: string + - description: Work History ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.WorkHistoryUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update Work History + tags: + - Work History +swagger: "2.0" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f21b842 --- /dev/null +++ b/go.mod @@ -0,0 +1,80 @@ +module narasi-ahli-be + +go 1.21.6 + +require ( + aidanwoods.dev/go-paseto v1.5.3 + github.com/Nerzal/gocloak/v13 v13.9.0 + github.com/arsmn/fiber-swagger/v2 v2.31.1 + github.com/efectn/fx-zerolog v1.1.0 + github.com/go-co-op/gocron v1.37.0 + github.com/go-playground/locales v0.14.1 + github.com/go-playground/universal-translator v0.18.1 + github.com/go-playground/validator/v10 v10.17.0 + github.com/gofiber/fiber/v2 v2.52.4 + github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/minio/minio-go/v7 v7.0.68 + github.com/pelletier/go-toml/v2 v2.1.1 + github.com/rs/zerolog v1.31.0 + github.com/swaggo/swag v1.16.3 + github.com/wneessen/go-mail v0.6.1 + go.uber.org/fx v1.20.1 + gorm.io/driver/postgres v1.5.4 + gorm.io/gorm v1.25.6 +) + +require ( + aidanwoods.dev/go-result v0.1.0 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/go-openapi/jsonpointer v0.20.3 // indirect + github.com/go-openapi/jsonreference v0.20.5 // indirect + github.com/go-openapi/spec v0.20.15 // indirect + github.com/go-openapi/swag v0.22.10 // indirect + github.com/go-resty/resty/v2 v2.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/philhofer/fwd v1.1.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/segmentio/ksuid v1.0.4 // indirect + github.com/swaggo/files v1.0.1 // indirect + github.com/tinylib/msgp v1.1.8 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.52.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..418901d --- /dev/null +++ b/go.sum @@ -0,0 +1,326 @@ +aidanwoods.dev/go-paseto v1.5.3 h1:y3pRY9MLWBhfO9VuCN0Bkyxa7Xmkt5coipYJfaOZgOs= +aidanwoods.dev/go-paseto v1.5.3/go.mod h1://T4uDrCXnzls7pKeCXaQ/zC3xv0KtgGMk4wnlOAHSs= +aidanwoods.dev/go-result v0.1.0 h1:y/BMIRX6q3HwaorX1Wzrjo3WUdiYeyWbvGe18hKS3K8= +aidanwoods.dev/go-result v0.1.0/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Nerzal/gocloak/v13 v13.9.0 h1:YWsJsdM5b0yhM2Ba3MLydiOlujkBry4TtdzfIzSVZhw= +github.com/Nerzal/gocloak/v13 v13.9.0/go.mod h1:YYuDcXZ7K2zKECyVP7pPqjKxx2AzYSpKDj8d6GuyM10= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/arsmn/fiber-swagger/v2 v2.31.1 h1:VmX+flXiGGNqLX3loMEEzL3BMOZFSPwBEWR04GA6Mco= +github.com/arsmn/fiber-swagger/v2 v2.31.1/go.mod h1:ZHhMprtB3M6jd2mleG03lPGhHH0lk9u3PtfWS1cBhMA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/efectn/fx-zerolog v1.1.0 h1:n/DYCo53t/mXhL6OasOI/4+JQCYa2doc1G3ogvTGoRY= +github.com/efectn/fx-zerolog v1.1.0/go.mod h1:j7ixjXFvkky0z4s7kX0Dz8O/D+E0TQo9uG+GHJijeqQ= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= +github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.20.3 h1:jykzYWS/kyGtsHfRt6aV8JTB9pcQAXPIA7qlZ5aRlyk= +github.com/go-openapi/jsonpointer v0.20.3/go.mod h1:c7l0rjoouAuIxCm8v/JWKRgMjDG/+/7UBWsXMrv6PsM= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.5 h1:hutI+cQI+HbSQaIGSfsBsYI0pHk+CATf8Fk5gCSj0yI= +github.com/go-openapi/jsonreference v0.20.5/go.mod h1:thAqAp31UABtI+FQGKAQfmv7DbFpKNUlva2UPCxKu2Y= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.15 h1:8bDcVxF607pTh9NpPwgsH4J5Uhh5mV5XoWnkurdiY+U= +github.com/go-openapi/spec v0.20.15/go.mod h1:o0upgqg5uYFG7O5mADrDVmSG3Wa6y6OLhwiCqQ+sTv4= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA= +github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= +github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofiber/fiber/v2 v2.31.0/go.mod h1:1Ega6O199a3Y7yDGuM9FyXDPYQfv+7/y48wl6WCwUF4= +github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= +github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= +github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.68 h1:hTqSIfLlpXaKuNy4baAp4Jjy2sqZEN9hRxD0M4aOfrQ= +github.com/minio/minio-go/v7 v7.0.68/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= +github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= +github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= +github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/wneessen/go-mail v0.6.1 h1:cDGqlGuEEhdILRe53VFzmM9WBk8Xh/QMvbO0oxrNJB4= +github.com/wneessen/go-mail v0.6.1/go.mod h1:G702XlFhzHV0Z4w9j2VsH5K9dJDvj0hx+yOOp1oX9vc= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo= +gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0= +gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A= +gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/hl.exe b/hl.exe new file mode 100644 index 0000000..2957141 Binary files /dev/null and b/hl.exe differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..38533b1 --- /dev/null +++ b/main.go @@ -0,0 +1,104 @@ +package main + +import ( + "narasi-ahli-be/app/database" + "narasi-ahli-be/app/middleware" + "narasi-ahli-be/app/module/activity_logs" + "narasi-ahli-be/app/module/advertisement" + "narasi-ahli-be/app/module/article_approvals" + "narasi-ahli-be/app/module/article_categories" + "narasi-ahli-be/app/module/article_category_details" + "narasi-ahli-be/app/module/article_comments" + "narasi-ahli-be/app/module/article_files" + "narasi-ahli-be/app/module/articles" + "narasi-ahli-be/app/module/cities" + "narasi-ahli-be/app/module/custom_static_pages" + "narasi-ahli-be/app/module/districts" + "narasi-ahli-be/app/module/education_history" + "narasi-ahli-be/app/module/feedbacks" + "narasi-ahli-be/app/module/magazine_files" + "narasi-ahli-be/app/module/magazines" + "narasi-ahli-be/app/module/master_menus" + "narasi-ahli-be/app/module/master_modules" + "narasi-ahli-be/app/module/provinces" + "narasi-ahli-be/app/module/research_journals" + "narasi-ahli-be/app/module/subscription" + "narasi-ahli-be/app/module/user_levels" + "narasi-ahli-be/app/module/user_role_accesses" + "narasi-ahli-be/app/module/user_role_level_details" + "narasi-ahli-be/app/module/user_roles" + "narasi-ahli-be/app/module/users" + "narasi-ahli-be/app/module/work_history" + "narasi-ahli-be/app/router" + "narasi-ahli-be/config/config" + "narasi-ahli-be/config/logger" + "narasi-ahli-be/config/webserver" + "time" + + fxzerolog "github.com/efectn/fx-zerolog" + "go.uber.org/fx" +) + +func main() { + + fx.New( + /* provide patterns */ + // config + fx.Provide(config.NewConfig), + // logging + fx.Provide(logger.NewLogger), + // fiber + fx.Provide(webserver.NewFiber), + // database + fx.Provide(database.NewDatabase), + // middleware + fx.Provide(middleware.NewMiddleware), + // data storage + fx.Provide(config.NewMinio), + // user management + fx.Provide(config.NewKeycloakConfig), + // router + fx.Provide(router.NewRouter), + // smtp config + fx.Provide(config.NewSmtpConfig), + + // provide modules + activity_logs.NewActivityLogsModule, + advertisement.NewAdvertisementModule, + article_categories.NewArticleCategoriesModule, + article_category_details.NewArticleCategoryDetailsModule, + article_files.NewArticleFilesModule, + article_approvals.NewArticleApprovalsModule, + articles.NewArticlesModule, + article_comments.NewArticleCommentsModule, + cities.NewCitiesModule, + custom_static_pages.NewCustomStaticPagesModule, + districts.NewDistrictsModule, + feedbacks.NewFeedbacksModule, + magazines.NewMagazinesModule, + magazine_files.NewMagazineFilesModule, + master_menus.NewMasterMenusModule, + master_modules.NewMasterModulesModule, + provinces.NewProvincesModule, + subscription.NewSubscriptionModule, + user_levels.NewUserLevelsModule, + user_roles.NewUserRolesModule, + user_role_accesses.NewUserRoleAccessesModule, + users.NewUsersModule, + user_role_level_details.NewUserRoleLevelDetailsModule, + education_history.NewEducationHistoryModule, + work_history.NewWorkHistoryModule, + research_journals.NewResearchJournalsModule, + + // start aplication + fx.Invoke(webserver.Start), + + // start scheduling + fx.Invoke(webserver.RunScheduling), + + // define logger + fx.WithLogger(fxzerolog.Init()), + + fx.StartTimeout(600*time.Second), + ).Run() +} diff --git a/plan/code-templates.md b/plan/code-templates.md new file mode 100644 index 0000000..2df25ca --- /dev/null +++ b/plan/code-templates.md @@ -0,0 +1,653 @@ +# Code Templates for Narasi Ahli Implementation + +## Database Entity Template + +### Example: Education History Entity +```go +package entity + +import ( + "narasi-ahli-be/app/database/entity/users" + "time" +) + +type EducationHistory struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null;index"` + SchoolName string `json:"school_name" gorm:"type:varchar(255);not null"` + Major string `json:"major" gorm:"type:varchar(255);not null"` + EducationLevel string `json:"education_level" gorm:"type:varchar(100);not null"` + GraduationYear int `json:"graduation_year" gorm:"type:int4;not null"` + CertificateImage *string `json:"certificate_image" gorm:"type:varchar(500)"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + + // Relationships + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} + +func (EducationHistory) TableName() string { + return "education_histories" +} +``` + +## Module Structure Template + +### Module File Template +```go +package education_history + +import ( + "narasi-ahli-be/app/module/education_history/controller" + "narasi-ahli-be/app/module/education_history/repository" + "narasi-ahli-be/app/module/education_history/service" + + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" +) + +type EducationHistoryRouter struct { + App fiber.Router + Controller *controller.Controller +} + +var NewEducationHistoryModule = fx.Options( + fx.Provide(repository.NewEducationHistoryRepository), + fx.Provide(service.NewEducationHistoryService), + fx.Provide(controller.NewController), + fx.Provide(NewEducationHistoryRouter), +) + +func NewEducationHistoryRouter(fiber *fiber.App, controller *controller.Controller) *EducationHistoryRouter { + return &EducationHistoryRouter{ + App: fiber, + Controller: controller, + } +} + +func (_i *EducationHistoryRouter) RegisterEducationHistoryRoutes() { + educationController := _i.Controller.EducationHistory + + _i.App.Route("/education-history", func(router fiber.Router) { + router.Get("/", educationController.All) + router.Get("/:id", educationController.Show) + router.Post("/", educationController.Save) + router.Put("/:id", educationController.Update) + router.Delete("/:id", educationController.Delete) + router.Post("/:id/certificate", educationController.UploadCertificate) + }) +} +``` + +### Controller Template +```go +package controller + +import ( + "narasi-ahli-be/app/middleware" + "narasi-ahli-be/app/module/education_history/request" + "narasi-ahli-be/app/module/education_history/service" + "narasi-ahli-be/utils/paginator" + utilRes "narasi-ahli-be/utils/response" + utilVal "narasi-ahli-be/utils/validator" + utilSvc "narasi-ahli-be/utils/service" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type educationHistoryController struct { + educationHistoryService service.EducationHistoryService +} + +type EducationHistoryController interface { + All(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error + UploadCertificate(c *fiber.Ctx) error +} + +func NewEducationHistoryController(educationHistoryService service.EducationHistoryService) EducationHistoryController { + return &educationHistoryController{ + educationHistoryService: educationHistoryService, + } +} + +// All Education History +// @Summary Get all Education History +// @Description API for getting all Education History for authenticated user +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param req query request.EducationHistoryQueryRequest false "query parameters" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history [get] +func (_i *educationHistoryController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + // Get user ID from auth token + authHeader := c.Get("Authorization") + userId := utilSvc.GetUserId(authHeader) + if userId == nil { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Code: 401, + Messages: utilRes.Messages{"Unauthorized"}, + }) + } + + reqContext := request.EducationHistoryQueryRequestContext{ + SchoolName: c.Query("schoolName"), + Major: c.Query("major"), + EducationLevel: c.Query("educationLevel"), + GraduationYear: c.Query("graduationYear"), + } + req := reqContext.ToParamRequest() + req.Pagination = paginate + + clientId := middleware.GetClientID(c) + educationData, paging, err := _i.educationHistoryService.All(clientId, *userId, req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history list successfully retrieved"}, + Data: educationData, + Meta: paging, + }) +} + +// Show Education History +// @Summary Get one Education History +// @Description API for getting one Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param id path int true "Education History ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history/{id} [get] +func (_i *educationHistoryController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + authHeader := c.Get("Authorization") + userId := utilSvc.GetUserId(authHeader) + if userId == nil { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Code: 401, + Messages: utilRes.Messages{"Unauthorized"}, + }) + } + + clientId := middleware.GetClientID(c) + educationData, err := _i.educationHistoryService.Show(clientId, *userId, uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history successfully retrieved"}, + Data: educationData, + }) +} + +// Save Education History +// @Summary Create Education History +// @Description API for create Education History +// @Tags Education History +// @Security Bearer +// @Param X-Client-Key header string false "Insert the X-Client-Key" +// @Param X-Csrf-Token header string true "Insert the X-Csrf-Token" +// @Param Authorization header string false "Insert your access token" default(Bearer ) +// @Param payload body request.EducationHistoryCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /education-history [post] +func (_i *educationHistoryController) Save(c *fiber.Ctx) error { + req := new(request.EducationHistoryCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + authHeader := c.Get("Authorization") + userId := utilSvc.GetUserId(authHeader) + if userId == nil { + return utilRes.Resp(c, utilRes.Response{ + Success: false, + Code: 401, + Messages: utilRes.Messages{"Unauthorized"}, + }) + } + + clientId := middleware.GetClientID(c) + dataResult, err := _i.educationHistoryService.Save(clientId, *userId, *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Education history successfully created"}, + Data: dataResult, + }) +} + +// Additional methods: Update, Delete, UploadCertificate... +``` + +### Service Template +```go +package service + +import ( + "narasi-ahli-be/app/module/education_history/mapper" + "narasi-ahli-be/app/module/education_history/repository" + "narasi-ahli-be/app/module/education_history/request" + "narasi-ahli-be/app/module/education_history/response" + "narasi-ahli-be/utils/paginator" + + "github.com/google/uuid" + "github.com/rs/zerolog" +) + +type educationHistoryService struct { + Repo repository.EducationHistoryRepository + Log zerolog.Logger +} + +type EducationHistoryService interface { + All(clientId *uuid.UUID, userId uint, req request.EducationHistoryQueryRequest) (educationHistories []*response.EducationHistoryResponse, paging paginator.Pagination, err error) + Show(clientId *uuid.UUID, userId uint, id uint) (educationHistory *response.EducationHistoryResponse, err error) + Save(clientId *uuid.UUID, userId uint, req request.EducationHistoryCreateRequest) (educationHistory *response.EducationHistoryResponse, err error) + Update(clientId *uuid.UUID, userId uint, id uint, req request.EducationHistoryUpdateRequest) (err error) + Delete(clientId *uuid.UUID, userId uint, id uint) error + UploadCertificate(clientId *uuid.UUID, userId uint, id uint, filePath string) error +} + +func NewEducationHistoryService(repo repository.EducationHistoryRepository, log zerolog.Logger) EducationHistoryService { + return &educationHistoryService{ + Repo: repo, + Log: log, + } +} + +func (_i *educationHistoryService) All(clientId *uuid.UUID, userId uint, req request.EducationHistoryQueryRequest) (educationHistories []*response.EducationHistoryResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(clientId, userId, req) + if err != nil { + return + } + + for _, result := range results { + educationHistories = append(educationHistories, mapper.EducationHistoryResponseMapper(result)) + } + + return +} + +func (_i *educationHistoryService) Show(clientId *uuid.UUID, userId uint, id uint) (educationHistory *response.EducationHistoryResponse, err error) { + result, err := _i.Repo.FindOneByUserAndId(clientId, userId, id) + if err != nil { + return nil, err + } + + return mapper.EducationHistoryResponseMapper(result), nil +} + +func (_i *educationHistoryService) Save(clientId *uuid.UUID, userId uint, req request.EducationHistoryCreateRequest) (educationHistory *response.EducationHistoryResponse, err error) { + _i.Log.Info().Interface("data", req).Msg("Creating education history") + + entity := req.ToEntity() + entity.UserID = userId + + result, err := _i.Repo.Create(clientId, entity) + if err != nil { + return nil, err + } + + return mapper.EducationHistoryResponseMapper(result), nil +} + +// Additional methods: Update, Delete, UploadCertificate... +``` + +### Repository Template +```go +package repository + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/education_history/request" + "narasi-ahli-be/utils/paginator" + + "github.com/google/uuid" + "gorm.io/gorm" +) + +type educationHistoryRepository struct { + DB *gorm.DB +} + +type EducationHistoryRepository interface { + GetAll(clientId *uuid.UUID, userId uint, req request.EducationHistoryQueryRequest) (educationHistories []*entity.EducationHistory, paging paginator.Pagination, err error) + FindOneByUserAndId(clientId *uuid.UUID, userId uint, id uint) (educationHistory *entity.EducationHistory, err error) + Create(clientId *uuid.UUID, educationHistory *entity.EducationHistory) (result *entity.EducationHistory, err error) + Update(clientId *uuid.UUID, userId uint, id uint, educationHistory *entity.EducationHistory) (err error) + Delete(clientId *uuid.UUID, userId uint, id uint) (err error) +} + +func NewEducationHistoryRepository(db *gorm.DB) EducationHistoryRepository { + return &educationHistoryRepository{ + DB: db, + } +} + +func (_i *educationHistoryRepository) GetAll(clientId *uuid.UUID, userId uint, req request.EducationHistoryQueryRequest) (educationHistories []*entity.EducationHistory, paging paginator.Pagination, err error) { + query := _i.DB.Where("user_id = ?", userId) + + // Apply filters + if req.SchoolName != nil { + query = query.Where("school_name ILIKE ?", "%"+*req.SchoolName+"%") + } + if req.Major != nil { + query = query.Where("major ILIKE ?", "%"+*req.Major+"%") + } + if req.EducationLevel != nil { + query = query.Where("education_level = ?", *req.EducationLevel) + } + if req.GraduationYear != nil { + query = query.Where("graduation_year = ?", *req.GraduationYear) + } + + // Include user relationship + query = query.Preload("User") + + // Apply pagination + paging = paginator.Paging(req.Pagination, query, &educationHistories) + + return +} + +func (_i *educationHistoryRepository) FindOneByUserAndId(clientId *uuid.UUID, userId uint, id uint) (educationHistory *entity.EducationHistory, err error) { + err = _i.DB.Where("user_id = ? AND id = ?", userId, id).Preload("User").First(&educationHistory).Error + return +} + +func (_i *educationHistoryRepository) Create(clientId *uuid.UUID, educationHistory *entity.EducationHistory) (result *entity.EducationHistory, err error) { + err = _i.DB.Create(educationHistory).Error + if err != nil { + return nil, err + } + + // Reload with relationships + err = _i.DB.Preload("User").First(&result, educationHistory.ID).Error + return +} + +// Additional methods: Update, Delete... +``` + +### Request DTO Template +```go +package request + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/utils/paginator" + "strconv" +) + +type EducationHistoryQueryRequest struct { + SchoolName *string `json:"schoolName"` + Major *string `json:"major"` + EducationLevel *string `json:"educationLevel"` + GraduationYear *int `json:"graduationYear"` + Pagination *paginator.Pagination `json:"pagination"` +} + +type EducationHistoryCreateRequest struct { + SchoolName string `json:"schoolName" validate:"required,min=2,max=255"` + Major string `json:"major" validate:"required,min=2,max=255"` + EducationLevel string `json:"educationLevel" validate:"required,min=2,max=100"` + GraduationYear int `json:"graduationYear" validate:"required,min=1950,max=2030"` + CertificateImage string `json:"certificateImage,omitempty"` +} + +func (req EducationHistoryCreateRequest) ToEntity() *entity.EducationHistory { + return &entity.EducationHistory{ + SchoolName: req.SchoolName, + Major: req.Major, + EducationLevel: req.EducationLevel, + GraduationYear: req.GraduationYear, + CertificateImage: &req.CertificateImage, + } +} + +type EducationHistoryUpdateRequest struct { + SchoolName string `json:"schoolName" validate:"required,min=2,max=255"` + Major string `json:"major" validate:"required,min=2,max=255"` + EducationLevel string `json:"educationLevel" validate:"required,min=2,max=100"` + GraduationYear int `json:"graduationYear" validate:"required,min=1950,max=2030"` + CertificateImage string `json:"certificateImage,omitempty"` +} + +func (req EducationHistoryUpdateRequest) ToEntity() *entity.EducationHistory { + return &entity.EducationHistory{ + SchoolName: req.SchoolName, + Major: req.Major, + EducationLevel: req.EducationLevel, + GraduationYear: req.GraduationYear, + CertificateImage: &req.CertificateImage, + } +} + +type EducationHistoryQueryRequestContext struct { + SchoolName string `json:"schoolName"` + Major string `json:"major"` + EducationLevel string `json:"educationLevel"` + GraduationYear string `json:"graduationYear"` +} + +func (req EducationHistoryQueryRequestContext) ToParamRequest() EducationHistoryQueryRequest { + var request EducationHistoryQueryRequest + + if schoolName := req.SchoolName; schoolName != "" { + request.SchoolName = &schoolName + } + if major := req.Major; major != "" { + request.Major = &major + } + if educationLevel := req.EducationLevel; educationLevel != "" { + request.EducationLevel = &educationLevel + } + if graduationYearStr := req.GraduationYear; graduationYearStr != "" { + graduationYear, err := strconv.Atoi(graduationYearStr) + if err == nil { + request.GraduationYear = &graduationYear + } + } + + return request +} +``` + +### Response DTO Template +```go +package response + +import ( + "time" +) + +type EducationHistoryResponse struct { + ID uint `json:"id"` + UserID uint `json:"userId"` + SchoolName string `json:"schoolName"` + Major string `json:"major"` + EducationLevel string `json:"educationLevel"` + GraduationYear int `json:"graduationYear"` + CertificateImage *string `json:"certificateImage"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + // Nested user info (optional) + User *UserBasicInfo `json:"user,omitempty"` +} + +type UserBasicInfo struct { + ID uint `json:"id"` + Username string `json:"username"` + Fullname string `json:"fullname"` + Email string `json:"email"` +} +``` + +### Mapper Template +```go +package mapper + +import ( + "narasi-ahli-be/app/database/entity" + "narasi-ahli-be/app/module/education_history/response" +) + +func EducationHistoryResponseMapper(educationHistory *entity.EducationHistory) *response.EducationHistoryResponse { + result := &response.EducationHistoryResponse{ + ID: educationHistory.ID, + UserID: educationHistory.UserID, + SchoolName: educationHistory.SchoolName, + Major: educationHistory.Major, + EducationLevel: educationHistory.EducationLevel, + GraduationYear: educationHistory.GraduationYear, + CertificateImage: educationHistory.CertificateImage, + CreatedAt: educationHistory.CreatedAt, + UpdatedAt: educationHistory.UpdatedAt, + } + + // Include user info if loaded + if educationHistory.User != nil { + result.User = &response.UserBasicInfo{ + ID: educationHistory.User.ID, + Username: educationHistory.User.Username, + Fullname: educationHistory.User.Fullname, + Email: educationHistory.User.Email, + } + } + + return result +} +``` + +## File Upload Service Template + +```go +package service + +import ( + "context" + "fmt" + "mime/multipart" + "path/filepath" + "strings" + "time" + + "github.com/google/uuid" + "github.com/minio/minio-go/v7" +) + +type FileUploadService interface { + UploadCertificate(file *multipart.FileHeader) (string, error) + UploadChatFile(file *multipart.FileHeader, messageType string) (string, error) + ValidateFile(file *multipart.FileHeader, allowedTypes []string, maxSize int64) error +} + +type fileUploadService struct { + minioClient *minio.Client + bucketName string +} + +func NewFileUploadService(minioClient *minio.Client, bucketName string) FileUploadService { + return &fileUploadService{ + minioClient: minioClient, + bucketName: bucketName, + } +} + +func (s *fileUploadService) UploadCertificate(file *multipart.FileHeader) (string, error) { + // Validate file + allowedTypes := []string{".jpg", ".jpeg", ".png", ".pdf"} + maxSize := int64(5 * 1024 * 1024) // 5MB + + if err := s.ValidateFile(file, allowedTypes, maxSize); err != nil { + return "", err + } + + // Generate unique filename + ext := filepath.Ext(file.Filename) + filename := fmt.Sprintf("certificates/%s_%d%s", uuid.New().String(), time.Now().Unix(), ext) + + // Upload to MinIO + src, err := file.Open() + if err != nil { + return "", err + } + defer src.Close() + + _, err = s.minioClient.PutObject( + context.Background(), + s.bucketName, + filename, + src, + file.Size, + minio.PutObjectOptions{ContentType: file.Header.Get("Content-Type")}, + ) + if err != nil { + return "", err + } + + return filename, nil +} + +func (s *fileUploadService) ValidateFile(file *multipart.FileHeader, allowedTypes []string, maxSize int64) error { + // Check file size + if file.Size > maxSize { + return fmt.Errorf("file size exceeds maximum allowed size") + } + + // Check file type + ext := strings.ToLower(filepath.Ext(file.Filename)) + allowed := false + for _, allowedType := range allowedTypes { + if ext == allowedType { + allowed = true + break + } + } + if !allowed { + return fmt.Errorf("file type not allowed") + } + + return nil +} +``` + +These templates provide a solid foundation for implementing each module following the existing codebase patterns. Each module can be built by copying and adapting these templates with the specific field requirements for that module. diff --git a/plan/database-migration-plan.md b/plan/database-migration-plan.md new file mode 100644 index 0000000..4f9b751 --- /dev/null +++ b/plan/database-migration-plan.md @@ -0,0 +1,408 @@ +# Database Migration Plan for Narasi Ahli + +## Migration Overview + +This document outlines the database schema changes and migration steps needed to implement the Narasi Ahli features. + +## Current Database State Analysis + +### Existing Users Table +The current `users` table already contains most of the required profile fields: +- ✅ `fullname` (nama lengkap) +- ✅ `email` (email) +- ✅ `phone_number` (can be used for WhatsApp) +- ✅ `last_education` (pendidikan terakhir) +- ❌ Missing: `degree` (gelar) +- ❌ Missing: `whatsapp_number` (separate from phone) +- ❌ Missing: `last_job_title` (pekerjaan terakhir) + +## Migration Steps + +### Step 1: Update Users Table + +```sql +-- Add new columns to users table +ALTER TABLE users +ADD COLUMN degree VARCHAR(100), +ADD COLUMN whatsapp_number VARCHAR(20), +ADD COLUMN last_job_title VARCHAR(255); + +-- Add indexes for better query performance +CREATE INDEX idx_users_whatsapp_number ON users(whatsapp_number); +CREATE INDEX idx_users_last_job_title ON users(last_job_title); +``` + +### Step 2: Create Education History Table + +```sql +CREATE TABLE education_histories ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + school_name VARCHAR(255) NOT NULL, + major VARCHAR(255) NOT NULL, + education_level VARCHAR(100) NOT NULL, + graduation_year INTEGER NOT NULL, + certificate_image VARCHAR(500), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_education_histories_user_id + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + +-- Indexes +CREATE INDEX idx_education_histories_user_id ON education_histories(user_id); +CREATE INDEX idx_education_histories_graduation_year ON education_histories(graduation_year); +CREATE INDEX idx_education_histories_education_level ON education_histories(education_level); + +-- Constraints +ALTER TABLE education_histories +ADD CONSTRAINT chk_graduation_year +CHECK (graduation_year >= 1950 AND graduation_year <= 2030); +``` + +### Step 3: Create Work History Table + +```sql +CREATE TABLE work_histories ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + job_title VARCHAR(255) NOT NULL, + company_name VARCHAR(255) NOT NULL, + start_date DATE NOT NULL, + end_date DATE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_work_histories_user_id + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + +-- Indexes +CREATE INDEX idx_work_histories_user_id ON work_histories(user_id); +CREATE INDEX idx_work_histories_start_date ON work_histories(start_date); +CREATE INDEX idx_work_histories_company_name ON work_histories(company_name); + +-- Constraints +ALTER TABLE work_histories +ADD CONSTRAINT chk_work_dates +CHECK (end_date IS NULL OR end_date >= start_date); +``` + +### Step 4: Create Research Journals Table + +```sql +CREATE TABLE research_journals ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + journal_title VARCHAR(500) NOT NULL, + publisher VARCHAR(255) NOT NULL, + journal_url VARCHAR(1000) NOT NULL, + published_date DATE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_research_journals_user_id + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + +-- Indexes +CREATE INDEX idx_research_journals_user_id ON research_journals(user_id); +CREATE INDEX idx_research_journals_publisher ON research_journals(publisher); +CREATE INDEX idx_research_journals_published_date ON research_journals(published_date); +``` + +### Step 5: Create Communication Tables + +```sql +-- Conversations table +CREATE TABLE conversations ( + id SERIAL PRIMARY KEY, + participant1_id INTEGER NOT NULL, + participant2_id INTEGER NOT NULL, + last_message_at TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_conversations_participant1 + FOREIGN KEY (participant1_id) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT fk_conversations_participant2 + FOREIGN KEY (participant2_id) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT chk_different_participants + CHECK (participant1_id != participant2_id) +); + +-- Unique constraint to prevent duplicate conversations +CREATE UNIQUE INDEX idx_conversations_participants +ON conversations(LEAST(participant1_id, participant2_id), GREATEST(participant1_id, participant2_id)); + +-- Indexes +CREATE INDEX idx_conversations_participant1 ON conversations(participant1_id); +CREATE INDEX idx_conversations_participant2 ON conversations(participant2_id); +CREATE INDEX idx_conversations_last_message_at ON conversations(last_message_at); + +-- Chat messages table +CREATE TABLE chat_messages ( + id SERIAL PRIMARY KEY, + conversation_id INTEGER NOT NULL, + sender_id INTEGER NOT NULL, + message_text TEXT, + message_type VARCHAR(20) NOT NULL DEFAULT 'text', + file_url VARCHAR(500), + file_name VARCHAR(255), + file_size BIGINT, + is_read BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_chat_messages_conversation + FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE, + CONSTRAINT fk_chat_messages_sender + FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT chk_message_type + CHECK (message_type IN ('text', 'image', 'file', 'audio')), + CONSTRAINT chk_message_content + CHECK ( + (message_type = 'text' AND message_text IS NOT NULL) OR + (message_type != 'text' AND file_url IS NOT NULL) + ) +); + +-- Indexes +CREATE INDEX idx_chat_messages_conversation_id ON chat_messages(conversation_id); +CREATE INDEX idx_chat_messages_sender_id ON chat_messages(sender_id); +CREATE INDEX idx_chat_messages_created_at ON chat_messages(created_at); +CREATE INDEX idx_chat_messages_is_read ON chat_messages(is_read); +``` + +### Step 6: Create AI Chat Tables + +```sql +-- AI Chat Sessions table +CREATE TABLE ai_chat_sessions ( + id SERIAL PRIMARY KEY, + ai_session_id VARCHAR(100) NOT NULL UNIQUE, + user_id INTEGER NOT NULL, + agent_id VARCHAR(100), + title VARCHAR(255) NOT NULL, + message_count INTEGER DEFAULT 0, + status VARCHAR(20) DEFAULT 'active', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_ai_chat_sessions_user + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT chk_session_status + CHECK (status IN ('active', 'archived', 'deleted')), + CONSTRAINT chk_message_count + CHECK (message_count >= 0) +); + +-- Indexes +CREATE INDEX idx_ai_chat_sessions_user_id ON ai_chat_sessions(user_id); +CREATE INDEX idx_ai_chat_sessions_status ON ai_chat_sessions(status); +CREATE INDEX idx_ai_chat_sessions_created_at ON ai_chat_sessions(created_at); + +-- AI Chat Messages table +CREATE TABLE ai_chat_messages ( + id SERIAL PRIMARY KEY, + session_id INTEGER NOT NULL, + message_type VARCHAR(20) NOT NULL, + content TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_ai_chat_messages_session + FOREIGN KEY (session_id) REFERENCES ai_chat_sessions(id) ON DELETE CASCADE, + CONSTRAINT chk_ai_message_type + CHECK (message_type IN ('user', 'assistant')) +); + +-- Indexes +CREATE INDEX idx_ai_chat_messages_session_id ON ai_chat_messages(session_id); +CREATE INDEX idx_ai_chat_messages_created_at ON ai_chat_messages(created_at); +CREATE INDEX idx_ai_chat_messages_message_type ON ai_chat_messages(message_type); +``` + +### Step 7: Create AI Chat Logs Table + +```sql +CREATE TABLE ai_chat_logs ( + id SERIAL PRIMARY KEY, + session_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + start_date TIMESTAMP NOT NULL, + end_date TIMESTAMP, + total_duration BIGINT DEFAULT 0, -- in seconds + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT fk_ai_chat_logs_session + FOREIGN KEY (session_id) REFERENCES ai_chat_sessions(id) ON DELETE CASCADE, + CONSTRAINT fk_ai_chat_logs_user + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT chk_chat_log_dates + CHECK (end_date IS NULL OR end_date >= start_date), + CONSTRAINT chk_total_duration + CHECK (total_duration >= 0) +); + +-- Indexes +CREATE INDEX idx_ai_chat_logs_session_id ON ai_chat_logs(session_id); +CREATE INDEX idx_ai_chat_logs_user_id ON ai_chat_logs(user_id); +CREATE INDEX idx_ai_chat_logs_start_date ON ai_chat_logs(start_date); +``` + +## GORM Entity Updates + +### Updated Users Entity + +```go +// Add to existing Users struct in app/database/entity/users/users.entity.go +type Users struct { + // ... existing fields ... + + // New fields for Narasi Ahli + Degree *string `json:"degree" gorm:"type:varchar(100)"` + WhatsappNumber *string `json:"whatsapp_number" gorm:"type:varchar(20);index"` + LastJobTitle *string `json:"last_job_title" gorm:"type:varchar(255);index"` + + // ... rest of existing fields ... +} +``` + +### Register New Entities in Database Models + +Update `app/database/index.database.go`: + +```go +func Models() []interface{} { + return []interface{}{ + // ... existing entities ... + + // New Narasi Ahli entities + entity.EducationHistory{}, + entity.WorkHistory{}, + entity.ResearchJournals{}, + entity.Conversations{}, + entity.ChatMessages{}, + entity.AIChatSessions{}, + entity.AIChatMessages{}, + entity.AIChatLogs{}, + } +} +``` + +## Data Migration Considerations + +### Existing Data Handling + +1. **Users Table Updates**: The new columns are nullable, so existing users won't be affected +2. **Profile Completion**: Consider adding a profile completion percentage field +3. **Data Validation**: Implement validation for phone number formats and email addresses + +### Migration Scripts + +```sql +-- Migration: 001_add_narasi_ahli_user_fields +-- Run this first +ALTER TABLE users +ADD COLUMN IF NOT EXISTS degree VARCHAR(100), +ADD COLUMN IF NOT EXISTS whatsapp_number VARCHAR(20), +ADD COLUMN IF NOT EXISTS last_job_title VARCHAR(255); + +-- Migration: 002_create_education_histories +-- Run after users table update +-- (Include the education_histories table creation from Step 2) + +-- Migration: 003_create_work_histories +-- (Include the work_histories table creation from Step 3) + +-- Migration: 004_create_research_journals +-- (Include the research_journals table creation from Step 4) + +-- Migration: 005_create_communication_tables +-- (Include both conversations and chat_messages tables from Step 5) + +-- Migration: 006_create_ai_chat_tables +-- (Include all AI chat related tables from Steps 6 and 7) +``` + +## Performance Optimization + +### Indexing Strategy + +1. **User-based queries**: All tables have user_id indexes +2. **Date-based queries**: Indexes on created_at, start_date, end_date +3. **Search functionality**: Indexes on searchable text fields +4. **Unique constraints**: Prevent duplicate conversations and AI sessions + +### Query Optimization + +1. **Pagination**: All list queries should use LIMIT/OFFSET +2. **Preloading**: Use GORM's Preload for related data +3. **Caching**: Consider Redis for frequently accessed data +4. **Connection pooling**: Optimize database connection settings + +## Security Considerations + +### Data Protection + +1. **User isolation**: All queries filter by user_id +2. **File upload validation**: Strict file type and size limits +3. **SQL injection prevention**: Use parameterized queries +4. **Data encryption**: Consider encrypting sensitive fields + +### Privacy Compliance + +1. **Data retention**: Implement data cleanup policies +2. **User consent**: Track user consent for data processing +3. **Data export**: Provide user data export functionality +4. **Right to delete**: Implement cascading deletes for user data + +## Rollback Plan + +### Rollback Steps + +1. **Remove new entities** from GORM Models() function +2. **Drop new tables** in reverse order: + ```sql + DROP TABLE IF EXISTS ai_chat_logs; + DROP TABLE IF EXISTS ai_chat_messages; + DROP TABLE IF EXISTS ai_chat_sessions; + DROP TABLE IF EXISTS chat_messages; + DROP TABLE IF EXISTS conversations; + DROP TABLE IF EXISTS research_journals; + DROP TABLE IF EXISTS work_histories; + DROP TABLE IF EXISTS education_histories; + ``` +3. **Remove new columns** from users table: + ```sql + ALTER TABLE users + DROP COLUMN IF EXISTS degree, + DROP COLUMN IF EXISTS whatsapp_number, + DROP COLUMN IF EXISTS last_job_title; + ``` + +### Backup Strategy + +1. **Full database backup** before migration +2. **Table-level backups** for critical tables +3. **Test restoration** on staging environment +4. **Monitor disk space** during migration + +## Testing Plan + +### Migration Testing + +1. **Unit tests** for each entity +2. **Integration tests** for relationships +3. **Performance tests** for large datasets +4. **Migration rollback tests** + +### Data Integrity Tests + +1. **Foreign key constraints** validation +2. **Check constraints** validation +3. **Unique constraints** validation +4. **Data type validation** + +This migration plan ensures a smooth transition to the new Narasi Ahli database schema while maintaining data integrity and performance. diff --git a/plan/implementation-checklist.md b/plan/implementation-checklist.md new file mode 100644 index 0000000..0cb998a --- /dev/null +++ b/plan/implementation-checklist.md @@ -0,0 +1,286 @@ +# Narasi Ahli Implementation Checklist + +## Database Schema Implementation + +### ✅ Phase 1A: Update Users Entity +- [ ] Modify `app/database/entity/users/users.entity.go` + - Add `Degree` field + - Add `WhatsappNumber` field + - Add `LastJobTitle` field + +### ✅ Phase 1B: Create New Entities + +#### Education History Entity +- [ ] Create `app/database/entity/education_history.entity.go` +- [ ] Fields: ID, UserID, SchoolName, Major, EducationLevel, GraduationYear, CertificateImage + +#### Work History Entity +- [ ] Create `app/database/entity/work_history.entity.go` +- [ ] Fields: ID, UserID, JobTitle, CompanyName, StartDate, EndDate + +#### Research Journals Entity +- [ ] Create `app/database/entity/research_journals.entity.go` +- [ ] Fields: ID, UserID, JournalTitle, Publisher, JournalURL, PublishedDate + +#### Communication Entities +- [ ] Create `app/database/entity/conversations.entity.go` +- [ ] Create `app/database/entity/chat_messages.entity.go` +- [ ] Support for text, image, file, audio message types + +#### AI Chat Entities +- [ ] Create `app/database/entity/ai_chat_sessions.entity.go` +- [ ] Create `app/database/entity/ai_chat_messages.entity.go` +- [ ] Create `app/database/entity/ai_chat_logs.entity.go` + +## Module Implementation + +### ✅ Phase 2A: Education History Module (`app/module/education_history/`) + +#### Files to Create: +- [ ] `education_history.module.go` - FX dependency injection setup +- [ ] `controller/controller.go` - Main controller struct +- [ ] `controller/education_history.controller.go` - HTTP handlers +- [ ] `service/education_history.service.go` - Business logic +- [ ] `repository/education_history.repository.go` - Data access +- [ ] `request/education_history.request.go` - Input DTOs +- [ ] `response/education_history.response.go` - Output DTOs +- [ ] `mapper/education_history.mapper.go` - Entity ↔ DTO conversion + +#### API Endpoints: +- [ ] `GET /education-history` - List with pagination +- [ ] `GET /education-history/:id` - Get by ID +- [ ] `POST /education-history` - Create new +- [ ] `PUT /education-history/:id` - Update existing +- [ ] `DELETE /education-history/:id` - Delete +- [ ] `POST /education-history/:id/certificate` - Upload certificate + +### ✅ Phase 2B: Work History Module (`app/module/work_history/`) + +#### Files to Create: +- [ ] `work_history.module.go` +- [ ] `controller/controller.go` +- [ ] `controller/work_history.controller.go` +- [ ] `service/work_history.service.go` +- [ ] `repository/work_history.repository.go` +- [ ] `request/work_history.request.go` +- [ ] `response/work_history.response.go` +- [ ] `mapper/work_history.mapper.go` + +#### API Endpoints: +- [ ] `GET /work-history` - List with pagination +- [ ] `GET /work-history/:id` - Get by ID +- [ ] `POST /work-history` - Create new +- [ ] `PUT /work-history/:id` - Update existing +- [ ] `DELETE /work-history/:id` - Delete + +### ✅ Phase 2C: Research Journals Module (`app/module/research_journals/`) + +#### Files to Create: +- [ ] `research_journals.module.go` +- [ ] `controller/controller.go` +- [ ] `controller/research_journals.controller.go` +- [ ] `service/research_journals.service.go` +- [ ] `repository/research_journals.repository.go` +- [ ] `request/research_journals.request.go` +- [ ] `response/research_journals.response.go` +- [ ] `mapper/research_journals.mapper.go` + +#### API Endpoints: +- [ ] `GET /research-journals` - List with pagination +- [ ] `GET /research-journals/:id` - Get by ID +- [ ] `POST /research-journals` - Create new +- [ ] `PUT /research-journals/:id` - Update existing +- [ ] `DELETE /research-journals/:id` - Delete + +### ✅ Phase 2D: Communications Module (`app/module/communications/`) + +#### Files to Create: +- [ ] `communications.module.go` +- [ ] `controller/controller.go` +- [ ] `controller/conversations.controller.go` +- [ ] `controller/chat_messages.controller.go` +- [ ] `service/conversations.service.go` +- [ ] `service/chat_messages.service.go` +- [ ] `service/file_upload.service.go` +- [ ] `repository/conversations.repository.go` +- [ ] `repository/chat_messages.repository.go` +- [ ] `request/conversations.request.go` +- [ ] `request/chat_messages.request.go` +- [ ] `response/conversations.response.go` +- [ ] `response/chat_messages.response.go` +- [ ] `mapper/conversations.mapper.go` +- [ ] `mapper/chat_messages.mapper.go` + +#### API Endpoints: +- [ ] `GET /conversations` - List user conversations +- [ ] `GET /conversations/:id` - Get conversation details +- [ ] `POST /conversations` - Start new conversation +- [ ] `GET /conversations/:id/messages` - Get messages with pagination +- [ ] `POST /conversations/:id/messages` - Send text message +- [ ] `POST /conversations/:id/messages/file` - Send file message +- [ ] `PUT /conversations/:id/messages/:messageId/read` - Mark as read + +### ✅ Phase 2E: AI Chat Module (`app/module/ai_chat/`) + +#### Files to Create: +- [ ] `ai_chat.module.go` +- [ ] `controller/controller.go` +- [ ] `controller/ai_chat_sessions.controller.go` +- [ ] `controller/ai_chat_messages.controller.go` +- [ ] `service/ai_chat_sessions.service.go` +- [ ] `service/ai_chat_messages.service.go` +- [ ] `service/ai_integration.service.go` +- [ ] `repository/ai_chat_sessions.repository.go` +- [ ] `repository/ai_chat_messages.repository.go` +- [ ] `request/ai_chat_sessions.request.go` +- [ ] `request/ai_chat_messages.request.go` +- [ ] `response/ai_chat_sessions.response.go` +- [ ] `response/ai_chat_messages.response.go` +- [ ] `mapper/ai_chat_sessions.mapper.go` +- [ ] `mapper/ai_chat_messages.mapper.go` + +#### API Endpoints: +- [ ] `GET /ai-chat/sessions` - List user sessions +- [ ] `POST /ai-chat/sessions` - Create new session +- [ ] `GET /ai-chat/sessions/:id` - Get session details +- [ ] `PUT /ai-chat/sessions/:id` - Update session (title, status) +- [ ] `DELETE /ai-chat/sessions/:id` - Delete session +- [ ] `GET /ai-chat/sessions/:id/messages` - Get messages +- [ ] `POST /ai-chat/sessions/:id/messages` - Send message to AI + +### ✅ Phase 2F: AI Chat Logs Module (`app/module/ai_chat_logs/`) + +#### Files to Create: +- [ ] `ai_chat_logs.module.go` +- [ ] `controller/controller.go` +- [ ] `controller/ai_chat_logs.controller.go` +- [ ] `service/ai_chat_logs.service.go` +- [ ] `repository/ai_chat_logs.repository.go` +- [ ] `request/ai_chat_logs.request.go` +- [ ] `response/ai_chat_logs.response.go` +- [ ] `mapper/ai_chat_logs.mapper.go` + +#### API Endpoints: +- [ ] `GET /ai-chat-logs` - List logs with filters +- [ ] `GET /ai-chat-logs/:id` - Get specific log +- [ ] `POST /ai-chat-logs` - Create log entry +- [ ] `PUT /ai-chat-logs/:id` - Update log (end session) + +## Integration Updates + +### ✅ Phase 3A: Database Registration +- [ ] Update `app/database/index.database.go` + - Add all new entities to `Models()` function + - Ensure proper migration order + +### ✅ Phase 3B: Router Registration +- [ ] Update `app/router/api.go` + - Add new router structs + - Add constructor parameters + - Add router registrations in `Register()` method + +### ✅ Phase 3C: Main Application Updates +- [ ] Update `main.go` + - Add new module imports + - Add module providers to FX + +### ✅ Phase 3D: Users Module Enhancement +- [ ] Update `app/module/users/request/users.request.go` + - Add new fields to request DTOs +- [ ] Update `app/module/users/response/users.response.go` + - Add new fields to response DTOs +- [ ] Update `app/module/users/mapper/users.mapper.go` + - Handle new field mappings + +## File Upload & Storage + +### ✅ Phase 4A: File Upload Service +- [ ] Create `utils/service/file_upload.service.go` +- [ ] Support certificate images (education) +- [ ] Support chat attachments (photos, files, audio) +- [ ] Integration with existing MinIO setup + +### ✅ Phase 4B: File Validation +- [ ] Implement file type validation +- [ ] Implement file size limits +- [ ] Implement security scanning + +## Real-time Features (Optional) + +### ✅ Phase 5A: WebSocket Support +- [ ] Add WebSocket handler for real-time chat +- [ ] Message broadcasting system +- [ ] Connection management + +### ✅ Phase 5B: Push Notifications +- [ ] New message notifications +- [ ] AI response notifications + +## Testing Implementation + +### ✅ Phase 6A: Unit Tests +- [ ] Service layer tests for each module +- [ ] Repository layer tests +- [ ] Mapper tests + +### ✅ Phase 6B: Integration Tests +- [ ] API endpoint tests +- [ ] Database integration tests +- [ ] File upload tests + +## Documentation + +### ✅ Phase 7A: API Documentation +- [ ] Update Swagger documentation for all endpoints +- [ ] Request/response examples +- [ ] Error response documentation + +### ✅ Phase 7B: Database Documentation +- [ ] ERD diagram +- [ ] Table relationships +- [ ] Migration scripts + +## Deployment Preparation + +### ✅ Phase 8A: Configuration +- [ ] Environment variables for AI service +- [ ] MinIO bucket configuration +- [ ] Database migration scripts + +### ✅ Phase 8B: Performance Optimization +- [ ] Database indexing +- [ ] Query optimization +- [ ] Caching strategy + +## Quality Assurance + +### ✅ Phase 9A: Code Review +- [ ] Follow existing code patterns +- [ ] Error handling consistency +- [ ] Security review + +### ✅ Phase 9B: Performance Testing +- [ ] Load testing for chat features +- [ ] File upload performance +- [ ] Database query performance + +--- + +## Implementation Notes + +1. **Follow Existing Patterns**: Each module should follow the exact same structure as existing modules +2. **Database Constraints**: Add proper foreign key constraints and indexes +3. **Error Handling**: Use consistent error response format +4. **Authentication**: Ensure all endpoints properly validate user tokens +5. **Authorization**: Users can only access their own data +6. **Pagination**: Implement pagination for all list endpoints +7. **File Security**: Validate file uploads and implement virus scanning +8. **API Versioning**: Consider version compatibility for future updates + +## Priority Order + +1. **High Priority**: Users profile updates, Education History, Work History +2. **Medium Priority**: Research Journals, Basic Communication +3. **Low Priority**: AI Chat, Real-time features, Advanced communication features + +This checklist ensures systematic implementation while maintaining code quality and consistency with the existing architecture. diff --git a/plan/narasi-ahli-development-plan.md b/plan/narasi-ahli-development-plan.md new file mode 100644 index 0000000..95479b7 --- /dev/null +++ b/plan/narasi-ahli-development-plan.md @@ -0,0 +1,354 @@ +# Narasi Ahli Backend Development Plan + +## Overview +This document outlines the development plan for implementing the Narasi Ahli backend features based on the existing codebase architecture and requirements. + +## Current Architecture Analysis + +### Existing Structure +- **Framework**: Go with Fiber web framework +- **Database**: PostgreSQL with GORM ORM +- **Architecture**: Clean Architecture with modules containing: + - Entity (Database models) + - Repository (Data access layer) + - Service (Business logic layer) + - Controller (HTTP handlers) + - Request/Response DTOs + - Mapper (Entity ↔ DTO conversion) +- **Dependency Injection**: Uber FX +- **Middleware**: Audit trails, CSRF, Client authentication +- **Documentation**: Swagger/OpenAPI + +### Current Users Entity Structure +The existing users entity already contains many required fields: +- ID, Username, Email, Fullname +- PhoneNumber, Address, WorkType, GenderType +- IdentityType, IdentityGroup, IdentityNumber +- DateOfBirth, LastEducation +- UserRoleId, UserLevelId, StatusId +- ProfilePicturePath, CreatedAt, UpdatedAt + +## Development Plan + +### Phase 1: Database Schema & Entity Updates + +#### 1.1 Update Users Entity for Profile Requirements +**File**: `app/database/entity/users/users.entity.go` + +**Required Fields to Add/Modify**: +- `Degree` (gelar) - string +- `WhatsappNumber` - string +- `LastJobTitle` (pekerjaan terakhir) - string + +**Changes Needed**: +```go +type Users struct { + // ... existing fields ... + Degree *string `json:"degree" gorm:"type:varchar"` // Gelar + WhatsappNumber *string `json:"whatsapp_number" gorm:"type:varchar"` // Nomor WhatsApp + LastJobTitle *string `json:"last_job_title" gorm:"type:varchar"` // Pekerjaan terakhir + // ... rest of existing fields ... +} +``` + +#### 1.2 Create New Entities + +**1.2.1 Education History Entity** +**File**: `app/database/entity/education_history.entity.go` +```go +type EducationHistory struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null"` + SchoolName string `json:"school_name" gorm:"type:varchar;not null"` + Major string `json:"major" gorm:"type:varchar;not null"` // Jurusan + EducationLevel string `json:"education_level" gorm:"type:varchar;not null"` // Tingkat pendidikan + GraduationYear int `json:"graduation_year" gorm:"type:int4;not null"` // Tahun lulus + CertificateImage *string `json:"certificate_image" gorm:"type:varchar"` // Ijazah image + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} +``` + +**1.2.2 Work History Entity** +**File**: `app/database/entity/work_history.entity.go` +```go +type WorkHistory struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null"` + JobTitle string `json:"job_title" gorm:"type:varchar;not null"` // Nama pekerjaan + CompanyName string `json:"company_name" gorm:"type:varchar;not null"` // Nama perusahaan + StartDate time.Time `json:"start_date" gorm:"not null"` // Tanggal mulai + EndDate *time.Time `json:"end_date"` // Tanggal selesai (nullable untuk pekerjaan saat ini) + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} +``` + +**1.2.3 Research Journals Entity** +**File**: `app/database/entity/research_journals.entity.go` +```go +type ResearchJournals struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserID uint `json:"user_id" gorm:"type:int4;not null"` + JournalTitle string `json:"journal_title" gorm:"type:varchar;not null"` // Nama judul jurnal + Publisher string `json:"publisher" gorm:"type:varchar;not null"` // Tempat publish + JournalURL string `json:"journal_url" gorm:"type:varchar;not null"` // URL jurnal + PublishedDate *time.Time `json:"published_date"` // Tanggal publikasi + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} +``` + +**1.2.4 Communication/Chat Entities** +**File**: `app/database/entity/communications.entity.go` +```go +type Conversations struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + Participant1ID uint `json:"participant1_id" gorm:"type:int4;not null"` + Participant2ID uint `json:"participant2_id" gorm:"type:int4;not null"` + LastMessageAt *time.Time `json:"last_message_at"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + Participant1 *users.Users `json:"participant1" gorm:"foreignKey:Participant1ID;references:ID"` + Participant2 *users.Users `json:"participant2" gorm:"foreignKey:Participant2ID;references:ID"` +} + +type ChatMessages struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + ConversationID uint `json:"conversation_id" gorm:"type:int4;not null"` + SenderID uint `json:"sender_id" gorm:"type:int4;not null"` + MessageText *string `json:"message_text" gorm:"type:text"` + MessageType string `json:"message_type" gorm:"type:varchar;not null;default:'text'"` // text, image, file, audio + FileURL *string `json:"file_url" gorm:"type:varchar"` // untuk file/image/audio + FileName *string `json:"file_name" gorm:"type:varchar"` + FileSize *int64 `json:"file_size" gorm:"type:bigint"` + IsRead bool `json:"is_read" gorm:"default:false"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + Conversation *Conversations `json:"conversation" gorm:"foreignKey:ConversationID;references:ID"` + Sender *users.Users `json:"sender" gorm:"foreignKey:SenderID;references:ID"` +} +``` + +**1.2.5 AI Chat Entities** +**File**: `app/database/entity/ai_chat.entity.go` +```go +type ChatSessions struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + AISessionID string `json:"ai_session_id" gorm:"type:varchar;not null;unique"` + UserID uint `json:"user_id" gorm:"type:int4;not null"` + AgentID *string `json:"agent_id" gorm:"type:varchar"` + Title string `json:"title" gorm:"type:varchar;not null"` + MessageCount int `json:"message_count" gorm:"type:int4;default:0"` + Status string `json:"status" gorm:"type:varchar;default:'active'"` // active, archived, deleted + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} + +type ChatMessages struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + SessionID uint `json:"session_id" gorm:"type:int4;not null"` + MessageType string `json:"message_type" gorm:"type:varchar;not null"` // user, assistant + Content string `json:"content" gorm:"type:text;not null"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + Session *ChatSessions `json:"session" gorm:"foreignKey:SessionID;references:ID"` +} +``` + +**1.2.6 AI Chat Logs Entity** +**File**: `app/database/entity/ai_chat_logs.entity.go` +```go +type AIChatLogs struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + SessionID uint `json:"session_id" gorm:"type:int4;not null"` + UserID uint `json:"user_id" gorm:"type:int4;not null"` + StartDate time.Time `json:"start_date" gorm:"not null"` + EndDate *time.Time `json:"end_date"` + TotalDuration int64 `json:"total_duration" gorm:"type:bigint"` // in seconds + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + Session *ChatSessions `json:"session" gorm:"foreignKey:SessionID;references:ID"` + User *users.Users `json:"user" gorm:"foreignKey:UserID;references:ID"` +} +``` + +### Phase 2: Module Implementation + +Each module will follow the existing pattern with: +- **Module file** (dependency injection setup) +- **Controller** (HTTP handlers) +- **Service** (business logic) +- **Repository** (data access) +- **Request DTOs** (input validation) +- **Response DTOs** (output formatting) +- **Mapper** (entity ↔ DTO conversion) + +#### 2.1 Education History Module +**Location**: `app/module/education_history/` + +**API Endpoints**: +- `GET /education-history` - Get all user's education history +- `GET /education-history/:id` - Get specific education record +- `POST /education-history` - Create new education record +- `PUT /education-history/:id` - Update education record +- `DELETE /education-history/:id` - Delete education record +- `POST /education-history/:id/certificate` - Upload certificate image + +#### 2.2 Work History Module +**Location**: `app/module/work_history/` + +**API Endpoints**: +- `GET /work-history` - Get all user's work history +- `GET /work-history/:id` - Get specific work record +- `POST /work-history` - Create new work record +- `PUT /work-history/:id` - Update work record +- `DELETE /work-history/:id` - Delete work record + +#### 2.3 Research Journals Module +**Location**: `app/module/research_journals/` + +**API Endpoints**: +- `GET /research-journals` - Get all user's research journals +- `GET /research-journals/:id` - Get specific journal +- `POST /research-journals` - Create new journal record +- `PUT /research-journals/:id` - Update journal record +- `DELETE /research-journals/:id` - Delete journal record + +#### 2.4 Communication Module +**Location**: `app/module/communications/` + +**API Endpoints**: +- `GET /conversations` - Get user's conversations +- `GET /conversations/:id` - Get specific conversation +- `POST /conversations` - Start new conversation +- `GET /conversations/:id/messages` - Get conversation messages +- `POST /conversations/:id/messages` - Send message +- `POST /conversations/:id/messages/file` - Send file message +- `PUT /conversations/:id/messages/:messageId/read` - Mark message as read + +#### 2.5 AI Chat Module +**Location**: `app/module/ai_chat/` + +**API Endpoints**: +- `GET /ai-chat/sessions` - Get user's AI chat sessions +- `POST /ai-chat/sessions` - Create new AI chat session +- `GET /ai-chat/sessions/:id` - Get specific session +- `PUT /ai-chat/sessions/:id` - Update session (title, status) +- `DELETE /ai-chat/sessions/:id` - Delete session +- `GET /ai-chat/sessions/:id/messages` - Get session messages +- `POST /ai-chat/sessions/:id/messages` - Send message to AI +- `PUT /ai-chat/sessions/:id/archive` - Archive session + +#### 2.6 AI Chat Logs Module +**Location**: `app/module/ai_chat_logs/` + +**API Endpoints**: +- `GET /ai-chat-logs` - Get user's chat logs +- `GET /ai-chat-logs/:id` - Get specific log +- `POST /ai-chat-logs` - Create new log entry +- `PUT /ai-chat-logs/:id` - Update log (end session) + +### Phase 3: Updated Users Module + +#### 3.1 Update Users Request/Response DTOs +**Files to modify**: +- `app/module/users/request/users.request.go` +- `app/module/users/response/users.response.go` +- `app/module/users/mapper/users.mapper.go` + +**New fields to handle**: +- Degree (gelar) +- WhatsappNumber (nomor whatsapp) +- LastJobTitle (pekerjaan terakhir) + +### Phase 4: Database Migrations & Router Updates + +#### 4.1 Update Database Models Registration +**File**: `app/database/index.database.go` + +Add new entities to the `Models()` function: +```go +entity.EducationHistory{}, +entity.WorkHistory{}, +entity.ResearchJournals{}, +entity.Conversations{}, +entity.ChatMessages{}, +entity.ChatSessions{}, +entity.AIChatLogs{}, +``` + +#### 4.2 Update Router Registration +**Files to modify**: +- `app/router/api.go` - Add new module routers +- `main.go` - Add new module imports and dependency injection + +### Phase 5: Implementation Order + +1. **Database Schema Updates** (Users entity modification) +2. **Education History Module** (Complete implementation) +3. **Work History Module** (Complete implementation) +4. **Research Journals Module** (Complete implementation) +5. **Communication Module** (Complete implementation) +6. **AI Chat Module** (Complete implementation) +7. **AI Chat Logs Module** (Complete implementation) +8. **Users Module Updates** (Profile API enhancements) +9. **Integration Testing** (All modules) +10. **Documentation Updates** (Swagger/API docs) + +### Phase 6: Additional Considerations + +#### 6.1 File Upload Handling +- Implement file upload service for certificate images +- Support for chat file attachments (images, documents, audio) +- Integration with existing MinIO storage + +#### 6.2 Real-time Features +- WebSocket support for real-time chat +- Push notifications for new messages + +#### 6.3 AI Integration +- External AI service integration +- API rate limiting for AI calls +- Response caching strategies + +#### 6.4 Security & Validation +- File upload validation (size, type restrictions) +- Message content validation +- User authorization for accessing conversations + +#### 6.5 Performance Optimization +- Database indexing for chat queries +- Pagination for message history +- Caching for frequently accessed data + +## API Documentation Structure + +Each module will include comprehensive Swagger documentation following the existing pattern: +- Endpoint descriptions +- Request/response schemas +- Authentication requirements +- Error response formats +- Example payloads + +## Testing Strategy + +- Unit tests for service layer +- Integration tests for API endpoints +- Database migration testing +- File upload testing +- Real-time communication testing + +## Deployment Considerations + +- Database migration scripts +- Environment-specific configurations +- MinIO bucket setup for file storage +- AI service endpoint configuration +- Performance monitoring setup + +--- + +This plan provides a comprehensive roadmap for implementing all the Narasi Ahli backend requirements while maintaining consistency with the existing codebase architecture and patterns. diff --git a/plan/old/init-db.js b/plan/old/init-db.js new file mode 100644 index 0000000..9c81975 --- /dev/null +++ b/plan/old/init-db.js @@ -0,0 +1,112 @@ +const Database = require('better-sqlite3'); +const path = require('path'); + +// Database file path +const dbPath = path.join(process.cwd(), 'lib', 'db', 'chat_history.db'); + +console.log('🚀 Initializing database at:', dbPath); + +// Initialize database +const db = new Database(dbPath); + +// Create tables if they don't exist +const initDatabase = () => { + console.log('📋 Creating tables...'); + + // Create chat_sessions table + db.exec(` + CREATE TABLE IF NOT EXISTS chat_sessions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT UNIQUE NOT NULL, + user_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + title TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + message_count INTEGER DEFAULT 0, + status TEXT DEFAULT 'active' + ) + `); + console.log('✅ chat_sessions table created'); + + // Create chat_messages table + db.exec(` + CREATE TABLE IF NOT EXISTS chat_messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT NOT NULL, + message_type TEXT NOT NULL CHECK (message_type IN ('user', 'assistant')), + content TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (session_id) REFERENCES chat_sessions (session_id) ON DELETE CASCADE + ) + `); + console.log('✅ chat_messages table created'); + + // Create indexes for better performance + db.exec(` + CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON chat_sessions (user_id); + CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON chat_sessions (agent_id); + CREATE INDEX IF NOT EXISTS idx_sessions_created_at ON chat_sessions (created_at); + CREATE INDEX IF NOT EXISTS idx_messages_session_id ON chat_messages (session_id); + CREATE INDEX IF NOT EXISTS idx_messages_created_at ON chat_messages (created_at); + `); + console.log('✅ Indexes created'); + + // Insert sample data (optional) + const sampleSession = { + session_id: 'sample-session-123', + user_id: 'user_123', + agent_id: 'd04bbced-ae93-4fb3-a015-9472e4e5e539', + title: 'Sample Chat Session' + }; + + const existingSession = db.prepare('SELECT id FROM chat_sessions WHERE session_id = ?').get(sampleSession.session_id); + + if (!existingSession) { + db.prepare(` + INSERT INTO chat_sessions (session_id, user_id, agent_id, title, message_count) + VALUES (?, ?, ?, ?, ?) + `).run(sampleSession.session_id, sampleSession.user_id, sampleSession.agent_id, sampleSession.title, 2); + + console.log('✅ Sample session inserted'); + + // Insert sample messages + const sampleMessages = [ + { type: 'user', content: 'Halo, ada yang bisa saya tanyakan?' }, + { type: 'assistant', content: 'Halo! Tentu saja, saya siap membantu Anda dengan pertanyaan apapun.' } + ]; + + const insertMessage = db.prepare(` + INSERT INTO chat_messages (session_id, message_type, content) + VALUES (?, ?, ?) + `); + + sampleMessages.forEach(msg => { + insertMessage.run(sampleSession.session_id, msg.type, msg.content); + }); + + console.log('✅ Sample messages inserted'); + } else { + console.log('ℹ️ Sample session already exists, skipping...'); + } +}; + +// Run initialization +try { + initDatabase(); + console.log('🎉 Database initialization completed successfully!'); + + // Show database info + const sessionCount = db.prepare('SELECT COUNT(*) as count FROM chat_sessions').get(); + const messageCount = db.prepare('SELECT COUNT(*) as count FROM chat_messages').get(); + + console.log('📊 Database Statistics:'); + console.log(` - Sessions: ${sessionCount.count}`); + console.log(` - Messages: ${messageCount.count}`); + +} catch (error) { + console.error('❌ Error initializing database:', error); +} finally { + db.close(); + console.log('🔒 Database connection closed'); +} \ No newline at end of file diff --git a/plan/old/route-chat-history.ts b/plan/old/route-chat-history.ts new file mode 100644 index 0000000..9c1ecbc --- /dev/null +++ b/plan/old/route-chat-history.ts @@ -0,0 +1,106 @@ +import { NextRequest, NextResponse } from 'next/server'; +import getDatabase from '@/lib/database'; + +interface ChatSession { + id: string; + session_id: string; + user_id: string; + agent_id: string; + title: string; + message_count: number; + created_at: string; + updated_at: string; +} + +interface ChatMessage { + id: string; + session_id: string; + message_type: string; + content: string; + created_at: string; +} + +// GET - Get session details by id +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const { id } = await params; + const db = getDatabase(); + + if (!id) { + return NextResponse.json({ error: 'ID is required' }, { status: 400 }); + } + + // Get session details by id + const session = db.prepare(` + SELECT * FROM chat_sessions + WHERE id = ? + `).get(id) as ChatSession | undefined; + + if (!session) { + return NextResponse.json({ error: 'Session not found' }, { status: 404 }); + } + + // Get messages for this session + const messages = db.prepare(` + SELECT * FROM chat_messages + WHERE session_id = ? + ORDER BY created_at ASC + `).all(session.session_id) as ChatMessage[]; + + return NextResponse.json({ + success: true, + session, + messages + }, { status: 200 }); + + } catch (error) { + console.error('Error fetching session details:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} + +// DELETE - Delete a specific chat session +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ id: string }> } +) { + try { + const { id } = await params; + const { searchParams } = new URL(request.url); + const user_id = searchParams.get('user_id'); + const db = getDatabase(); + + if (!id) { + return NextResponse.json({ error: 'ID is required' }, { status: 400 }); + } + + if (!user_id) { + return NextResponse.json({ error: 'User ID is required' }, { status: 400 }); + } + + // Check if session exists and belongs to user + const session = db.prepare(` + SELECT id FROM chat_sessions + WHERE id = ? AND user_id = ? + `).get(id, user_id) as { id: string } | undefined; + + if (!session) { + return NextResponse.json({ error: 'Session not found or access denied' }, { status: 404 }); + } + + // Delete session (messages will be deleted automatically due to CASCADE) + db.prepare('DELETE FROM chat_sessions WHERE id = ?').run(id); + + return NextResponse.json({ + success: true, + message: 'Session deleted successfully' + }, { status: 200 }); + + } catch (error) { + console.error('Error deleting session:', error); + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/plan/old/route-chat-history1.ts b/plan/old/route-chat-history1.ts new file mode 100644 index 0000000..fb9b1ec --- /dev/null +++ b/plan/old/route-chat-history1.ts @@ -0,0 +1,117 @@ +import { NextRequest, NextResponse } from 'next/server'; +import getDatabase from '@/lib/database'; + +// POST - Save chat history +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { user_id, agent_id, session_id, title, messages } = body; + const db = getDatabase(); + + // Validate required fields + if (!user_id || !agent_id || !session_id) { + return NextResponse.json( + { error: 'Missing required fields: user_id, agent_id, session_id' }, + { status: 400 } + ); + } + + // Check if session already exists + const existingSession = db.prepare( + 'SELECT id FROM chat_sessions WHERE session_id = ?' + ).get(session_id); + + if (existingSession) { + // Update existing session + db.prepare(` + UPDATE chat_sessions + SET title = ?, updated_at = CURRENT_TIMESTAMP, message_count = ? + WHERE session_id = ? + `).run(title || 'Chat Session', messages?.length || 0, session_id); + } else { + // Create new session + db.prepare(` + INSERT INTO chat_sessions (session_id, user_id, agent_id, title, message_count) + VALUES (?, ?, ?, ?, ?) + `).run(session_id, user_id, agent_id, title || 'Chat Session', messages?.length || 0); + } + + // Save messages if provided + if (messages && Array.isArray(messages)) { + // Delete existing messages for this session + db.prepare('DELETE FROM chat_messages WHERE session_id = ?').run(session_id); + + // Insert new messages + const insertMessage = db.prepare(` + INSERT INTO chat_messages (session_id, message_type, content) + VALUES (?, ?, ?) + `); + + const insertMany = db.transaction((messages: any[]) => { + for (const message of messages) { + insertMessage.run(session_id, message.type, message.content); + } + }); + + insertMany(messages); + } + + return NextResponse.json( + { + success: true, + message: 'Chat history saved successfully', + session_id + }, + { status: 200 } + ); + + } catch (error) { + console.error('Error saving chat history:', error); + return NextResponse.json( + { error: 'Internal server error' }, + { status: 500 } + ); + } +} + +// GET - Get chat history +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const user_id = searchParams.get('user_id'); + const agent_id = searchParams.get('agent_id'); + const session_id = searchParams.get('session_id'); + const db = getDatabase(); + + // Validate required fields + if (!user_id) { + return NextResponse.json( + { error: 'Missing required parameter: user_id' }, + { status: 400 } + ); + } + + let sessions; + let params: any[] = [user_id]; + + // Get all sessions for user (simplified - only based on user_id) + sessions = db.prepare(` + SELECT * + FROM chat_sessions + WHERE user_id = ? + ORDER BY id DESC + `).all(user_id); + + return NextResponse.json({ + sessions, + total: sessions.length + }); + + } catch (error) { + console.error('Error getting chat history:', error); + return NextResponse.json( + { error: 'Internal server error' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..1c60de2 --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +``` +go get -v ./... +go mod vendor -v + +swag init -g main.go --output docs/swagger +go run main.go +``` \ No newline at end of file diff --git a/remove_client_id_entities.ps1 b/remove_client_id_entities.ps1 new file mode 100644 index 0000000..040837d --- /dev/null +++ b/remove_client_id_entities.ps1 @@ -0,0 +1,66 @@ +# Script to remove client_id from all entity files +Write-Host "Starting client_id removal from entity files..." -ForegroundColor Green + +# List of entity files that have client_id +$entityFiles = @( + "app/database/entity/users/users.entity.go", + "app/database/entity/article_category_details/article_category_details.entity.go", + "app/database/entity/advertisement.entity.go", + "app/database/entity/activity_logs.entity.go", + "app/database/entity/articles.entity.go", + "app/database/entity/article_approvals.entity.go", + "app/database/entity/article_comments.entity.go", + "app/database/entity/audit_trails.entity.go", + "app/database/entity/article_files.entity.go", + "app/database/entity/article_categories.entity.go", + "app/database/entity/csrf_token_records.entity.go", + "app/database/entity/feedbacks.entity.go", + "app/database/entity/forgot_passwords.entity.go", + "app/database/entity/magazines.entity.go", + "app/database/entity/master_modules.entity.go", + "app/database/entity/one_time_passwords.entity.go", + "app/database/entity/magazine_files.entity.go", + "app/database/entity/master_menus.entity.go", + "app/database/entity/user_roles.entity.go", + "app/database/entity/subscription.entity.go", + "app/database/entity/user_levels.entity.go", + "app/database/entity/user_role_level_details.entity.go", + "app/database/entity/user_role_accesses.entity.go" +) + +$processedFiles = 0 +$totalFiles = $entityFiles.Count + +foreach ($filePath in $entityFiles) { + if (Test-Path $filePath) { + $processedFiles++ + Write-Progress -Activity "Processing entity files" -Status "Processing $([System.IO.Path]::GetFileName($filePath))" -PercentComplete (($processedFiles / $totalFiles) * 100) + + Write-Host "Processing: $filePath" -ForegroundColor Yellow + + $content = Get-Content $filePath -Raw + + # Remove ClientId field definitions + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*', '' + + # Remove ClientId field without json tag + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s*', '' + + # Remove uuid import if no more uuid usage + if ($content -notmatch 'uuid\.') { + $content = $content -replace '"github\.com/google/uuid"\s*\n', '' + $content = $content -replace 'github\.com/google/uuid\s*\n', '' + } + + # Clean up extra empty lines + $content = $content -replace '\n\s*\n\s*\n', "`n`n" + + # Write back to file + Set-Content -Path $filePath -Value $content -NoNewline + } +} + +Write-Host "Entity client_id removal process completed!" -ForegroundColor Green +Write-Host "Processed $processedFiles entity files" -ForegroundColor Cyan diff --git a/remove_client_id_entities_v2.ps1 b/remove_client_id_entities_v2.ps1 new file mode 100644 index 0000000..0466d8c --- /dev/null +++ b/remove_client_id_entities_v2.ps1 @@ -0,0 +1,68 @@ +# Enhanced script to remove client_id from entity files +Write-Host "Starting enhanced client_id removal from entity files..." -ForegroundColor Green + +# List of entity files that have client_id +$entityFiles = @( + "app/database/entity/users/users.entity.go", + "app/database/entity/article_category_details/article_category_details.entity.go", + "app/database/entity/advertisement.entity.go", + "app/database/entity/activity_logs.entity.go", + "app/database/entity/articles.entity.go", + "app/database/entity/article_approvals.entity.go", + "app/database/entity/article_comments.entity.go", + "app/database/entity/audit_trails.entity.go", + "app/database/entity/article_files.entity.go", + "app/database/entity/article_categories.entity.go", + "app/database/entity/csrf_token_records.entity.go", + "app/database/entity/feedbacks.entity.go", + "app/database/entity/forgot_passwords.entity.go", + "app/database/entity/magazines.entity.go", + "app/database/entity/master_modules.entity.go", + "app/database/entity/one_time_passwords.entity.go", + "app/database/entity/magazine_files.entity.go", + "app/database/entity/master_menus.entity.go", + "app/database/entity/user_roles.entity.go", + "app/database/entity/subscription.entity.go", + "app/database/entity/user_levels.entity.go", + "app/database/entity/user_role_level_details.entity.go", + "app/database/entity/user_role_accesses.entity.go" +) + +$processedFiles = 0 +$totalFiles = $entityFiles.Count + +foreach ($filePath in $entityFiles) { + if (Test-Path $filePath) { + $processedFiles++ + Write-Progress -Activity "Processing entity files" -Status "Processing $([System.IO.Path]::GetFileName($filePath))" -PercentComplete (($processedFiles / $totalFiles) * 100) + + Write-Host "Processing: $filePath" -ForegroundColor Yellow + + $content = Get-Content $filePath -Raw + + # More specific patterns to remove ClientId field + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s*', '' + + # Remove ClientId field with different patterns + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*', '' + + # Remove uuid import if no more uuid usage + if ($content -notmatch 'uuid\.') { + $content = $content -replace '"github\.com/google/uuid"\s*\n', '' + $content = $content -replace 'github\.com/google/uuid\s*\n', '' + } + + # Clean up extra empty lines + $content = $content -replace '\n\s*\n\s*\n', "`n`n" + + # Write back to file + Set-Content -Path $filePath -Value $content -NoNewline + } +} + +Write-Host "Enhanced entity client_id removal process completed!" -ForegroundColor Green +Write-Host "Processed $processedFiles entity files" -ForegroundColor Cyan diff --git a/remove_client_id_final.ps1 b/remove_client_id_final.ps1 new file mode 100644 index 0000000..a0c0ffd --- /dev/null +++ b/remove_client_id_final.ps1 @@ -0,0 +1,33 @@ +# Final script to remove ClientId from all entity files +Write-Host "Final removal of ClientId from entity files..." -ForegroundColor Green + +# Get all .go files in entity directory +$entityFiles = Get-ChildItem -Path "app/database/entity" -Recurse -Filter "*.go" + +foreach ($file in $entityFiles) { + $content = Get-Content $file.FullName -Raw + + # Skip if file doesn't contain ClientId + if ($content -notmatch "ClientId") { + continue + } + + Write-Host "Processing: $($file.Name)" -ForegroundColor Yellow + + # Remove ClientId field with regex + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s+`json:"client_id"[^`]*`\s*', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s*\n', '' + $content = $content -replace '\s*ClientId\s+\*uuid\.UUID\s*', '' + + # Remove uuid import if no more uuid usage + if ($content -notmatch 'uuid\.') { + $content = $content -replace '"github\.com/google/uuid"\s*\n', '' + $content = $content -replace 'github\.com/google/uuid\s*\n', '' + } + + # Write back to file + Set-Content -Path $file.FullName -Value $content -NoNewline +} + +Write-Host "Final ClientId removal completed!" -ForegroundColor Green diff --git a/remove_client_id_v2.ps1 b/remove_client_id_v2.ps1 new file mode 100644 index 0000000..0df826e --- /dev/null +++ b/remove_client_id_v2.ps1 @@ -0,0 +1,79 @@ +# Enhanced script to remove client_id usage from remaining files +Write-Host "Starting enhanced client_id removal process..." -ForegroundColor Green + +# List of files that still have client_id usage +$problemFiles = @( + "app/module/custom_static_pages/repository/custom_static_pages.repository.go", + "app/module/article_files/repository/article_files.repository.go", + "app/module/article_comments/repository/article_comments.repository.go", + "app/module/article_categories/repository/article_categories.repository.go", + "app/module/articles/repository/articles.repository.go", + "app/module/advertisement/repository/advertisement.repository.go", + "app/module/activity_logs/repository/activity_logs.repository.go", + "app/module/users/service/users.service.go", + "app/module/subscription/repository/subscription.repository.go", + "app/module/subscription/controller/subscription.controller.go", + "app/module/subscription/service/subscription.service.go", + "app/module/magazines/controller/magazines.controller.go", + "app/module/magazines/repository/magazines.repository.go", + "app/module/magazines/service/magazines.service.go", + "app/module/users/controller/users.controller.go", + "app/module/feedbacks/service/feedbacks.service.go", + "app/module/feedbacks/repository/feedbacks.repository.go", + "app/module/feedbacks/controller/feedbacks.controller.go" +) + +foreach ($filePath in $problemFiles) { + if (Test-Path $filePath) { + Write-Host "Processing: $filePath" -ForegroundColor Yellow + + $content = Get-Content $filePath -Raw + + # More aggressive replacements + $content = $content -replace 'clientId \*uuid\.UUID,?\s*', '' + $content = $content -replace 'clientId \*uuid\.UUID\s*,?\s*', '' + $content = $content -replace 'clientId \*uuid\.UUID\s*', '' + + # Remove clientId assignments + $content = $content -replace 'clientId := middleware\.GetClientID\(c\)\s*\n\s*', '' + $content = $content -replace 'clientId := middleware\.GetClientID\(c\)', '' + + # Remove clientId from function calls - be more specific + $content = $content -replace '\(clientId,', '(' + $content = $content -replace ', clientId\)', ')' + $content = $content -replace '\(clientId\)', '()' + + # Remove client_id filters + $content = $content -replace 'if clientId != nil \{\s*\n\s*query = query\.Where\("client_id = \?", clientId\)\s*\n\s*\}', '' + $content = $content -replace 'if clientId != nil \{\s*\n\s*query\.Where\("client_id = \?", clientId\)\s*\n\s*\}', '' + + # Remove clientId logging + $content = $content -replace '_i\.Log\.Info\(\)\.Interface\("clientId", clientId\)\.Msg\(""\)\s*\n\s*', '' + + # Remove clientId comments + $content = $content -replace '// Add ClientId filter\s*\n', '' + $content = $content -replace '// Get ClientId from context\s*\n', '' + + # Clean up function signatures + $content = $content -replace ',\s*,', ',' + $content = $content -replace '\(\s*,', '(' + $content = $content -replace ',\s*\)', ')' + $content = $content -replace '\(\s*\)', '()' + + # Remove unused imports + if ($content -notmatch 'uuid\.') { + $content = $content -replace '"github\.com/google/uuid"\s*\n', '' + $content = $content -replace 'github\.com/google/uuid\s*\n', '' + } + + if ($content -notmatch 'middleware\.') { + $content = $content -replace '"narasi-ahli-be/app/middleware"\s*\n', '' + $content = $content -replace 'narasi-ahli-be/app/middleware\s*\n', '' + } + + # Write back to file + Set-Content -Path $filePath -Value $content -NoNewline + } +} + +Write-Host "Enhanced client ID removal process completed!" -ForegroundColor Green diff --git a/storage/ascii_art.txt b/storage/ascii_art.txt new file mode 100644 index 0000000..040b6c0 --- /dev/null +++ b/storage/ascii_art.txt @@ -0,0 +1,9 @@ + +______ _ _ _____ _ _ +| ___(_) | / ___| | | | +| |_ _| |__ ___ _ __ \ `--.| |_ __ _ _ __| |_ ___ _ __ +| _| | | '_ \ / _ \ '__| `--. \ __/ _` | '__| __/ _ \ '__| +| | | | |_) | __/ | /\__/ / || (_| | | | || __/ | +\_| |_|_.__/ \___|_| \____/ \__\__,_|_| \__\___|_| + + diff --git a/storage/private.go b/storage/private.go new file mode 100644 index 0000000..c4af254 --- /dev/null +++ b/storage/private.go @@ -0,0 +1,8 @@ +package storage + +import ( + "embed" +) + +//go:embed private/* +var Private embed.FS diff --git a/storage/private/example.html b/storage/private/example.html new file mode 100644 index 0000000..f8f360d --- /dev/null +++ b/storage/private/example.html @@ -0,0 +1 @@ +Example html file for private storage. \ No newline at end of file diff --git a/storage/public/example.txt b/storage/public/example.txt new file mode 100644 index 0000000..15e7931 --- /dev/null +++ b/storage/public/example.txt @@ -0,0 +1 @@ +Example txt file for public storage. \ No newline at end of file diff --git a/utils/index.utils.go b/utils/index.utils.go new file mode 100644 index 0000000..4d891a2 --- /dev/null +++ b/utils/index.utils.go @@ -0,0 +1,17 @@ +package utils + +import "github.com/gofiber/fiber/v2" + +func IsEnabled(key bool) func(c *fiber.Ctx) bool { + if key { + return nil + } + + return func(c *fiber.Ctx) bool { return true } +} + +func CsrfErrorHandler(c *fiber.Ctx, err error) error { + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ + "error": "CSRF protection: " + err.Error(), + }) +} diff --git a/utils/paginator/index.paginator.go b/utils/paginator/index.paginator.go new file mode 100644 index 0000000..9b7823a --- /dev/null +++ b/utils/paginator/index.paginator.go @@ -0,0 +1,72 @@ +package paginator + +import ( + "math" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +const ( + defaultLimit = 10 + defaultSort = "desc" + defaultSortBy = "id" +) + +type Pagination struct { + Limit int `json:"limit,omitempty"` + Offset int `json:"-"` + Page int `json:"page,omitempty"` + NextPage int `json:"nextPage,omitempty"` + PreviousPage int `json:"previousPage,omitempty"` + Count int64 `json:"count,omitempty"` + TotalPage int `json:"totalPage,omitempty"` + Sort string `json:"sort,omitempty"` + SortBy string `json:"sortBy,omitempty"` +} + +func Paging(p *Pagination) *Pagination { + p.TotalPage = int(math.Ceil(float64(p.Count) / float64(p.Limit))) + if p.Page > 1 { + p.PreviousPage = p.Page - 1 + } else { + p.PreviousPage = p.Page + } + if p.Page == p.TotalPage { + p.NextPage = p.Page + } else { + p.NextPage = p.Page + 1 + } + return p +} + +func Paginate(c *fiber.Ctx) (*Pagination, error) { + limit, err := strconv.Atoi(c.Query("limit")) + if err != nil { + limit = defaultLimit + } + page, err := strconv.Atoi(c.Query("page")) + if err != nil { + page = 1 + } + sort := c.Query("sort") + if sort == "" { + sort = defaultSort + } + sortBy := c.Query("sortBy") + if sortBy == "" { + sortBy = defaultSortBy + } + p := &Pagination{ + Limit: limit, + Page: page, + Sort: sort, + SortBy: sortBy, + } + if p.Page == 0 { + p.Page = 1 + } + + p.Offset = (p.Page - 1) * p.Limit + return p, nil +} diff --git a/utils/response/error.response.go b/utils/response/error.response.go new file mode 100644 index 0000000..7c831bb --- /dev/null +++ b/utils/response/error.response.go @@ -0,0 +1,19 @@ +package response + +type BadRequestError struct { + Success bool `json:"success" example:"false"` + Code int `json:"code" example:"400"` + Message string `json:"message" example:"bad request"` +} + +type UnauthorizedError struct { + Success bool `json:"success" example:"false"` + Code int `json:"code" example:"401"` + Message string `json:"message" example:"unauthorized access"` +} + +type InternalServerError struct { + Success bool `json:"success" example:"false"` + Code int `json:"code" example:"500"` + Message string `json:"message" example:"internal server error"` +} diff --git a/utils/response/index.response.go b/utils/response/index.response.go new file mode 100644 index 0000000..2bdae4e --- /dev/null +++ b/utils/response/index.response.go @@ -0,0 +1,103 @@ +package response + +import ( + "fmt" + val "narasi-ahli-be/utils/validator" + "strings" + + validator "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog/log" +) + +// Alias for any slice +type Messages = []any + +type Error struct { + Success bool `json:"success"` + Code int `json:"code"` + Message any `json:"message"` +} + +// error makes it compatible with the error interface +func (e *Error) Error() string { + return fmt.Sprint(e.Message) +} + +// A struct to return normal response +type Response struct { + Success bool `json:"success" example:"true"` + Code int `json:"code" example:"200"` + Messages Messages `json:"messages"` + Data any `json:"data,omitempty"` + Meta any `json:"meta,omitempty"` +} + +// nothiing to describe this fucking variable +var IsProduction bool + +// Default error handler +var ErrorHandler = func(c *fiber.Ctx, err error) error { + resp := Response{ + Code: fiber.StatusInternalServerError, + } + + // handle errors + if c, ok := err.(validator.ValidationErrors); ok { + resp.Code = fiber.StatusUnprocessableEntity + resp.Messages = Messages{removeTopStruct(c.Translate(val.Trans))} + } else if c, ok := err.(*fiber.Error); ok { + resp.Code = c.Code + resp.Messages = Messages{c.Message} + } else if c, ok := err.(*Error); ok { + resp.Code = c.Code + resp.Messages = Messages{c.Message} + + // for ent and other errors + if resp.Messages == nil { + resp.Messages = Messages{err} + } + } else { + resp.Messages = Messages{err.Error()} + } + + if !IsProduction { + log.Error().Err(err).Msg("From: Fiber's error handler") + } + + return Resp(c, resp) +} + +// function to return pretty json response +func Resp(c *fiber.Ctx, resp Response) error { + // set status + if resp.Code == 0 { + resp.Code = fiber.StatusOK + } + c.Status(resp.Code) + // return response + return c.JSON(resp) +} + +// remove unecessary fields from validator message +func removeTopStruct(fields map[string]string) map[string]string { + res := map[string]string{} + + for field, msg := range fields { + stripStruct := field[strings.Index(field, ".")+1:] + res[stripStruct] = msg + } + + return res +} + +func Unauthorized() *Response { + return &Response{ + Success: false, + Code: 401, + Data: nil, + Messages: Messages{ + "Unauthorized", + }, + } +} diff --git a/utils/service/string.service.go b/utils/service/string.service.go new file mode 100644 index 0000000..d753f93 --- /dev/null +++ b/utils/service/string.service.go @@ -0,0 +1,64 @@ +package service + +import ( + "crypto/rand" + "encoding/json" + "math/big" + "strings" + "unicode" +) + +func IsNumeric(str string) bool { + for _, char := range str { + if !unicode.IsDigit(char) { + return false + } + } + return true +} + +func MakeSlug(s string) string { + // Convert to lowercase + s = strings.ToLower(s) + + // Replace spaces with hyphens + s = strings.ReplaceAll(s, " ", "-") + + // Remove non-alphanumeric characters + return strings.Map(func(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' { + return r + } + return -1 + }, s) +} + +func GenerateNumericCode(codeLength int) (string, error) { + const digits = "0123456789" + result := make([]byte, codeLength) + + for i := 0; i < codeLength; i++ { + num, err := rand.Int(rand.Reader, big.NewInt(int64(len(digits)))) + if err != nil { + return "", err + } + result[i] = digits[num.Int64()] + } + + return string(result), nil +} + +func StructToMap(obj interface{}) (map[string]interface{}, error) { + var result map[string]interface{} + jsonData, err := json.Marshal(obj) + if err != nil { + return nil, err + } + + err = json.Unmarshal(jsonData, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/utils/service/user_utils.service.go b/utils/service/user_utils.service.go new file mode 100644 index 0000000..60715f9 --- /dev/null +++ b/utils/service/user_utils.service.go @@ -0,0 +1,43 @@ +package service + +import ( + "narasi-ahli-be/app/database/entity/users" + "narasi-ahli-be/app/module/users/repository" + "strings" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/rs/zerolog" +) + +func GetUserInfo(log zerolog.Logger, repo repository.UsersRepository, bearerToken string) *users.Users { + tokenString := strings.TrimPrefix(bearerToken, "Bearer ") + token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) + if err != nil { + return nil + } + claims := token.Claims.(jwt.MapClaims) + sub := claims["sub"].(string) + + user, err := repo.FindByKeycloakId(sub) + if err != nil { + return nil + } + + log.Info().Str("timestamp", time.Now(). + Format(time.RFC3339)).Str("Service:GetUserInfo", "UserInfo:GetUserInfo"). + Interface("payload", user).Msg("") + + return user +} + +func GetUserId(bearerToken string) *string { + tokenString := strings.TrimPrefix(bearerToken, "Bearer ") + token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) + if err != nil { + return nil + } + claims := token.Claims.(jwt.MapClaims) + sub := claims["sub"].(string) + return &sub +} diff --git a/utils/validator/index.validator.go b/utils/validator/index.validator.go new file mode 100644 index 0000000..d68aafd --- /dev/null +++ b/utils/validator/index.validator.go @@ -0,0 +1,63 @@ +package response + +import ( + "github.com/go-playground/locales/en" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" + ent "github.com/go-playground/validator/v10/translations/en" + "github.com/gofiber/fiber/v2" + "github.com/rs/zerolog/log" + "reflect" +) + +var ( + validate *validator.Validate + uni *ut.UniversalTranslator + Trans ut.Translator +) + +func init() { + validate = validator.New() + + uni = ut.New(en.New()) + Trans, _ = uni.GetTranslator("en") + + if err := ent.RegisterDefaultTranslations(validate, Trans); err != nil && !fiber.IsChild() { + log.Panic().Err(err).Msg("") + } +} + +func ValidateStruct(input any) error { + return validate.Struct(input) +} + +func ParseBody(c *fiber.Ctx, body any) error { + if err := c.BodyParser(body); err != nil { + return err + } + + return nil +} + +func ParseAndValidate(c *fiber.Ctx, body any) error { + v := reflect.ValueOf(body) + + switch v.Kind() { + case reflect.Ptr: + err := ParseBody(c, body) + if err != nil { + return err + } + + return ValidateStruct(v.Elem().Interface()) + case reflect.Struct: + err := ParseBody(c, &body) + if err != nil { + return err + } + + return ValidateStruct(v) + default: + return nil + } +}