Skip to content

Commit

Permalink
Merge branch 'main' of github.com:rakechen-0307/Taipei-City-Dashboard…
Browse files Browse the repository at this point in the history
… into getLocation
  • Loading branch information
rakechen-0307 committed Jun 1, 2024
2 parents 4a79b8c + c977c6e commit f27d823
Show file tree
Hide file tree
Showing 72 changed files with 1,707 additions and 954 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# <img src='Taipei-City-Dashboard-FE/src/assets/images/TUIC.svg' height='28'> Taipei City Dashboard

> This branch is for routine maintenance of Taipei City Dashboard from 4/22 - 5/12 due to Taipei Codefest 2024. For the latest released version of Taipei City Dashboard, please visit the `main` branch.
## Introduction

Taipei City Dashboard is a data visualization platform developed by [Taipei Urban Intelligence Center (TUIC)](https://tuic.gov.taipei/en).
Expand Down
9 changes: 8 additions & 1 deletion Taipei-City-Dashboard-BE/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,19 @@ func StartApplication() {

// 3. Add common middlewares that need to run on all routes
routes.Router.Use(middleware.AddCommonHeaders)
// routes.Router.Use(cors.New(cors.Config{
// AllowOrigins: []string{"https://tuic.gov.taipei"},
// AllowMethods: []string{"GET"},
// AllowHeaders: []string{"Origin"},
// ExposeHeaders: []string{"Content-Length"},
// AllowCredentials: true,
// }))

// 4. Configure routes and routing groups (./router.go)
routes.ConfigureRoutes()

// 5. Configure http server
addr := global.GinAddr
addr := global.GinAddr

err := endless.ListenAndServe(addr, routes.Router)
if err != nil {
Expand Down
119 changes: 119 additions & 0 deletions Taipei-City-Dashboard-BE/app/controllers/contributor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package controllers

import (
"net/http"
"strconv"

"TaipeiCityDashboardBE/app/models"

"github.com/gin-gonic/gin"
)

/*
GetAllContributors returns the contributor information
GET /api/v1/contributor
*/
func GetAllContributors(c *gin.Context) {
type contributorQuery struct {
PageSize int `form:"pagesize"`
PageNum int `form:"pagenum"`
Sort string `form:"sort"`
Order string `form:"order"`
}

// Get query parameters
var query contributorQuery
c.ShouldBindQuery(&query)

contributors, totalContributors, err := models.GetAllContributors(query.PageSize, query.PageNum, query.Sort, query.Order)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"status": "success", "total": totalContributors, "data": contributors})
}

/*
CreateContributor creates a new contributor
POST /api/v1/contributor
*/
func CreateContributor(c *gin.Context) {
var contributor models.Contributor

// Bind the contributor data
if err := c.ShouldBindJSON(&contributor); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": err.Error()})
return
}

if contributor.UserID == "" || contributor.UserName == "" || contributor.Image == "" || contributor.Link == "" {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "user_id, user_name, image and link info is required"})
return
}

contributor, err := models.CreateContributor(contributor.UserID, contributor.UserName, contributor.Image, contributor.Link, contributor.Identity, contributor.Description, contributor.Include)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"status": "success", "data": contributor})
}

/*
UpdateContributor updates the contributor information
PATCH /api/v1/contributor/:id
*/
func UpdateContributor(c *gin.Context) {
ID, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid contributor ID"})
return
}

// 1. Check if the contributor exists
contributor, err := models.GetContributorByID(ID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "No contributor found"})
return
}

// 2. Bind the JSON body to the contributor struct
err = c.ShouldBindJSON(&contributor)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 3. Update the contributor
contributor, err = models.UpdateContributor(ID, contributor.UserID, contributor.UserName, contributor.Image, contributor.Link, contributor.Identity, contributor.Description, contributor.Include)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update contributor"})
return
}

c.JSON(http.StatusOK, gin.H{"status": "success", "data": contributor})
}

/*
DeleteContributor deletes a contributor from the database.
DELETE /api/v1/contributor/:id
*/
func DeleteContributor(c *gin.Context) {
// 1. Get the contributor ID from the context
ID, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid contributor ID"})
return
}

// 2. Delete the contributor
contributorStatus, err := models.DeleteContributorByID(ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"status": "success", "contributor_deleted": contributorStatus})
}
2 changes: 1 addition & 1 deletion Taipei-City-Dashboard-BE/app/middleware/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// AddCommonHeaders adds common headers that will be appended to all requests.
func AddCommonHeaders(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
// c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH, DELETE")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
Expand Down
4 changes: 2 additions & 2 deletions Taipei-City-Dashboard-BE/app/models/componentConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Component struct {
ChartConfig json.RawMessage `json:"chart_config" gorm:"type:json"`
MapFilter json.RawMessage `json:"map_filter" gorm:"column:map_filter;type:json"`
TimeFrom string `json:"time_from" gorm:"column:time_from;type:varchar"`
TimeTo string `json:"time_to" gorm:"column:time_to;type:varchar"`
TimeTo *string `json:"time_to" gorm:"column:time_to;type:varchar"`
UpdateFreq *int64 `json:"update_freq" gorm:"column:update_freq;type:integer"`
UpdateFreqUnit string `json:"update_freq_unit" gorm:"column:update_freq_unit;type:varchar"`
Source string `json:"source" gorm:"column:source;type:varchar"`
Expand Down Expand Up @@ -145,7 +145,7 @@ func GetComponentByID(id int) (component Component, err error) {
return component, nil
}

func UpdateComponent(id int, name string, historyConfig json.RawMessage, mapFilter json.RawMessage, timeFrom string, timeTo string, updateFreq *int64, updateFreqUnit string, source string, shortDesc string, longDesc string, useCase string, links pq.StringArray, contributors pq.StringArray) (component Component, err error) {
func UpdateComponent(id int, name string, historyConfig json.RawMessage, mapFilter json.RawMessage, timeFrom string, timeTo *string, updateFreq *int64, updateFreqUnit string, source string, shortDesc string, longDesc string, useCase string, links pq.StringArray, contributors pq.StringArray) (component Component, err error) {
component = Component{Name: name, HistoryConfig: historyConfig, MapFilter: mapFilter, TimeFrom: timeFrom, TimeTo: timeTo, UpdateFreq: updateFreq, UpdateFreqUnit: updateFreqUnit, Source: source, ShortDesc: shortDesc, LongDesc: longDesc, UseCase: useCase, Links: links, Contributors: contributors, UpdatedAt: time.Now()}

err = DBManager.Table("components").Where("id = ?", id).Updates(&component).Error
Expand Down
94 changes: 94 additions & 0 deletions Taipei-City-Dashboard-BE/app/models/contributor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package models

import (
"time"
)

/* ----- Models ----- */

type Contributor struct {
ID int64 `json:"id" gorm:"column:id;autoincrement;primaryKey"`
UserID string `json:"user_id" gorm:"column:user_id;type:varchar;not null"`
UserName string `json:"user_name" gorm:"column:user_name;type:varchar;not null"`
Image string `json:"image" gorm:"column:image;type:text"`
Link string `json:"link" gorm:"column:link;type:text;not null"`
Identity *string `json:"identity" gorm:"column:identity;type:varchar"`
Description *string `json:"description" gorm:"column:description;type:text"`
Include *bool `json:"include" gorm:"column:include;type:boolean;default:false;not null"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;type:timestamp with time zone;not null"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;type:timestamp with time zone;not null"`
}

/* ----- Handlers ----- */

func GetAllContributors(pageSize int, pageNum int, sort string, order string) (contributors []Contributor, totalContributors int64, err error) {
tempDB := DBManager.Table("contributors")

// Count the total amount of contributors
tempDB.Count(&totalContributors)

// Sort the contributors
if sort != "" {
tempDB = tempDB.Order("contributors." + sort + " " + order)
} else {
tempDB = tempDB.Order("contributors.id asc")
}

// Paginate the contributors
if pageSize > 0 {
tempDB = tempDB.Limit(pageSize)
if pageNum > 0 {
tempDB = tempDB.Offset((pageNum - 1) * pageSize)
}
}

// Get the contributors
err = tempDB.Find(&contributors).Error

return contributors, totalContributors, err
}

func GetContributorByID(ID int) (contributor Contributor, err error) {
err = DBManager.Table("contributors").Where("id = ?", ID).First(&contributor).Error
return contributor, err
}

func CreateContributor(userID string, userName string, image string, link string, identity *string, description *string, include *bool) (contributor Contributor, err error) {
contributor.UserID = userID
contributor.UserName = userName
contributor.Image = image
contributor.Link = link
contributor.CreatedAt = time.Now()
contributor.UpdatedAt = time.Now()
contributor.Identity = identity
contributor.Description = description
contributor.Include = include
err = DBManager.Create(&contributor).Error
return contributor, err
}

func UpdateContributor(ID int, userID string, userName string, image string, link string, identity *string, description *string, include *bool) (contributor Contributor, err error) {
err = DBManager.Model(&Contributor{}).Where("id = ?", ID).Updates(map[string]interface{}{
"user_id": userID, "user_name": userName, "image": image, "link": link, "updated_at": time.Now(),
"identity": identity, "description": description, "include": include,
}).Error
if err != nil {
return contributor, err
}

err = DBManager.Table("contributors").Where("id = ?", ID).First(&contributor).Error
if err != nil {
return contributor, err
}

return contributor, err
}

func DeleteContributorByID(ID int) (contributorStatus bool, err error) {
err = DBManager.Model(&Contributor{}).Where("id = ?", ID).Delete(&Contributor{}).Error
if err != nil {
return false, err
}

return true, err
}
1 change: 1 addition & 0 deletions Taipei-City-Dashboard-BE/app/models/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func MigrateManagerSchema() {
DBManager.AutoMigrate(&AuthUser{}, &Role{}, &Group{})
DBManager.AutoMigrate(&AuthUserGroupRole{})
DBManager.AutoMigrate(&Component{}, &ComponentChart{}, &ComponentMap{})
DBManager.AutoMigrate(&Contributor{})
DBManager.AutoMigrate(&Dashboard{}, &DashboardGroup{}, &Issue{})
DBManager.AutoMigrate(&Incident{})

Expand Down
15 changes: 15 additions & 0 deletions Taipei-City-Dashboard-BE/app/routes/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func ConfigureRoutes() {
configureIssueRoutes()
configureIncidentRoutes()
// configureWsRoutes()
configureContributorRoutes()
}

func configureAuthRoutes() {
Expand Down Expand Up @@ -149,3 +150,17 @@ func configureIncidentRoutes() {
// wsRoutes.PUT("/write/", controllers.WriteMap)
// }
// }
func configureContributorRoutes() {
contributorRoutes := RouterGroup.Group("/contributor")
contributorRoutes.Use(middleware.LimitAPIRequests(global.ContributorLimitAPIRequestsTimes, global.LimitRequestsDuration))
contributorRoutes.Use(middleware.LimitTotalRequests(global.ContributorLimitTotalRequestsTimes, global.TokenExpirationDuration))
{
contributorRoutes.GET("/", controllers.GetAllContributors)
}
contributorRoutes.Use(middleware.IsSysAdm())
{
contributorRoutes.POST("/", controllers.CreateContributor)
contributorRoutes.PATCH("/:id", controllers.UpdateContributor)
contributorRoutes.DELETE("/:id", controllers.DeleteContributor)
}
}
24 changes: 13 additions & 11 deletions Taipei-City-Dashboard-BE/global/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ const (
VERSION = "v1"

// Request api limit times and duration
AuthLimitAPIRequestsTimes = 300
AuthLimitTotalRequestsTimes = 600
UserLimitAPIRequestsTimes = 100
UserLimitTotalRequestsTimes = 500
ComponentLimitAPIRequestsTimes = 200
ComponentLimitTotalRequestsTimes = 1000
DashboardLimitAPIRequestsTimes = 200
DashboardLimitTotalRequestsTimes = 1000
IssueLimitAPIRequestsTimes = 20
IssueLimitTotalRequestsTimes = 200
LimitRequestsDuration = 60 * time.Second
AuthLimitAPIRequestsTimes = 300
AuthLimitTotalRequestsTimes = 600
UserLimitAPIRequestsTimes = 100
UserLimitTotalRequestsTimes = 500
ComponentLimitAPIRequestsTimes = 200
ComponentLimitTotalRequestsTimes = 1000
ContributorLimitAPIRequestsTimes = 100
ContributorLimitTotalRequestsTimes = 500
DashboardLimitAPIRequestsTimes = 200
DashboardLimitTotalRequestsTimes = 1000
IssueLimitAPIRequestsTimes = 20
IssueLimitTotalRequestsTimes = 200
LimitRequestsDuration = 60 * time.Second

// JWT Issuer
JwtIssuer = "Taipei citydashboard"
Expand Down
2 changes: 1 addition & 1 deletion Taipei-City-Dashboard-BE/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,4 @@ gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
14 changes: 7 additions & 7 deletions Taipei-City-Dashboard-FE/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Taipei-City-Dashboard-FE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@vueuse/core": "^10.7.2",
"apexcharts": "^3.45.2",
"axios": "^1.6.5",
"city-dashboard-component": "^1.0.2",
"city-dashboard-component": "^1.0.3",
"dayjs": "^1.11.10",
"lodash.debounce": "^4.0.8",
"mapbox-gl": "^3.1.0",
Expand Down
Loading

0 comments on commit f27d823

Please sign in to comment.