LCOV - code coverage report
Current view: top level - adapter/http/router - router.go Coverage Total Hit
Test: coverage.lcov Lines: 95.0 % 159 151
Test Date: 2026-04-14 06:42:22 Functions: - 0 0

            Line data    Source code
       1              : // Package router はアプリケーションのHTTPルーティングを定義します。
       2              : package router
       3              : 
       4              : import (
       5              :         "log/slog"
       6              :         "net/http"
       7              : 
       8              :         "github.com/gin-gonic/gin"
       9              : 
      10              :         fba "resume/internal/adapter/gateway/firebase"
      11              :         "resume/internal/adapter/http/controller"
      12              :         "resume/internal/adapter/http/middleware"
      13              :         "resume/internal/domain/repository"
      14              :         uci18n "resume/internal/usecase/i18n"
      15              : )
      16              : 
      17              : // New はアプリケーションのルーティングを初期化し、*gin.Engine を返します。
      18              : func New(
      19              :         fb fba.Auth,
      20              :         appLog *slog.Logger,
      21              :         ucI18n uci18n.Usecase,
      22              :         userRepo repository.UserRepository,
      23              :         hSample *controller.SampleHandler,
      24              :         hTest *controller.TestHandler,
      25              :         hUser *controller.UserHandler,
      26              :         hAuth *controller.AuthHandler,
      27              :         hProfile *controller.ProfileHandler,
      28              :         hMeta *controller.MetaHandler,
      29              :         hI18n *controller.I18nHandler,
      30              :         hEducation *controller.EducationHandler,
      31              :         hSkill *controller.SkillHandler,
      32              :         hContact *controller.ContactHandler,
      33              :         hLicense *controller.LicenseHandler,
      34            1 : ) *gin.Engine {
      35            1 :         r := gin.New()
      36            1 :         r.RedirectTrailingSlash = false // 末尾スラッシュ補正をやめる
      37            1 :         r.RedirectFixedPath = false     // 大文字小文字などの補正もやめる
      38            1 :         r.Use(middleware.CamelSnakeCodec())
      39            1 :         r.Use(gin.Recovery())
      40            1 :         r.Use(middleware.Cors(middleware.DefaultCORSConfig()))
      41            1 :         r.Use(middleware.InjectAuth(fb))
      42            1 : 
      43            1 :         r.Use(middleware.RequestLogger(appLog, &middleware.RequestLoggerOptions{
      44            2 :                 Skipper:        func(c *gin.Context) bool { return c.Request.URL.Path == "/health" },
      45              :                 IncludeHeaders: []string{"X-Request-ID", "X-Trace-Id"},
      46              :         }))
      47              : 
      48              :         // region 全ルーター対策
      49              :         // 404が返らなかった対策
      50            1 :         r.NoRoute(func(c *gin.Context) {
      51            0 :                 c.JSON(http.StatusNotFound, gin.H{
      52            0 :                         "error": "not found",
      53            0 :                 })
      54            0 :         })
      55              :         // メソッド違いに対応出来てなかった対策
      56            1 :         r.HandleMethodNotAllowed = true
      57            1 :         r.NoMethod(func(c *gin.Context) {
      58            0 :                 c.JSON(http.StatusMethodNotAllowed, gin.H{
      59            0 :                         "error": "method not allowed",
      60            0 :                 })
      61            0 :         })
      62              :         // cors対策 任意:OPTIONS を一括ハンドル(プリフライトを確実に 204 で返す)
      63              :         //r.OPTIONS("/*path", func(c *gin.Context) {
      64              :         //      c.Status(204)
      65              :         //})
      66              :         // endregion 全ルーター対策
      67              : 
      68              :         // region i18n
      69              : 
      70              :         // (b) ロケール交渉ミドルウェア(?lang, cookie=lang, Accept-Language)
      71            1 :         src := NewUsecaseLocaleSource(ucI18n)
      72            1 :         r.Use(middleware.Middleware(middleware.Opts{
      73            1 :                 Default:   "ja",
      74            1 :                 Allow:     nil, // 増やしたらここに追記
      75            1 :                 Query:     "lang",
      76            1 :                 Header:    "Accept-Language",
      77            1 :                 Cookie:    "", // Cookieを使わないなら空
      78            1 :                 SetHeader: true,
      79            1 :                 Store:     src,
      80            1 :         }))
      81            1 : 
      82            1 :         i18nG := r.Group("/i18n")
      83            2 :         {
      84            1 :                 // (c) バンドル配信用ハンドラ(ETag/304 対応)
      85            1 :                 i18nG.GET("/bundle", hI18n.GetBundle)
      86            1 :                 i18nG.POST("/reload", hI18n.Reload)
      87            1 :                 i18nG.GET("/list", hI18n.ListLocales)
      88            1 :         }
      89              : 
      90              :         // endregion i18n
      91              : 
      92              :         // region テスト
      93              :         // ヘルスチェック
      94            2 :         r.GET("/health", func(c *gin.Context) { c.String(http.StatusOK, "ok") })
      95              : 
      96              :         // テスト
      97            1 :         r.GET("ping", hSample.Ping)
      98            1 :         testG := r.Group("test")
      99            1 :         testG.Use(middleware.RequireAuth(fb))
     100            2 :         {
     101            1 :                 testG.GET("1", hTest.Ping)
     102            1 :         }
     103              :         // endregion テスト
     104              : 
     105              :         // region 認証
     106            1 :         authG := r.Group("/auth")
     107            2 :         {
     108            1 :                 authG.POST("/fl/login", hAuth.UserLogin)
     109            1 :         }
     110              :         // endregion 認証
     111              : 
     112              :         // region firebase認可
     113            1 :         resolveUserMw := middleware.NewResolveUser(userRepo)
     114            1 :         // フリーランサー情報
     115            1 :         freelanceG := r.Group("/fl")
     116            1 :         freelanceG.Use(middleware.RequireAuth(fb))
     117            1 :         freelanceG.Use(resolveUserMw.RequireResolveUser())
     118            2 :         {
     119            1 :                 // ログインしている自身の情報
     120            1 :                 profileG := freelanceG.Group("/profile")
     121            1 :                 profileG.GET("/", hProfile.Me)
     122            1 :                 profileG.GET("/fba", hProfile.Fba)
     123            1 :                 profileG.GET("/identities", hProfile.Identities)
     124            1 : 
     125            1 :                 // 履歴書掲載情報の氏名など
     126            1 :                 personalG := freelanceG.Group("/personal")
     127            1 :                 personalG.GET("/", hProfile.GetPersonalInfo)
     128            1 :                 personalG.PATCH("/", hProfile.PatchPersonalInfo)
     129            1 :                 personalG.GET("/exists", hProfile.HasPersonalInfo)
     130            1 :                 //PATCH  /profile/account             # メールやパスワードなどアカウント設定(任意)
     131            1 : 
     132            1 :                 addressG := freelanceG.Group("/address")
     133            1 :                 addressG.GET("/exists", hProfile.HasUserAddress)
     134            1 :                 addressG.GET("/", hProfile.ListUserAddress)
     135            1 :                 addressG.GET("/:address_id", hProfile.DetailUserAddress)
     136            1 :                 addressG.POST("/", hProfile.CreateUserAddress)
     137            1 :                 addressG.PATCH("/:address_id", hProfile.UpdateUserAddress)
     138            1 :                 addressG.DELETE("/:address_id", hProfile.DeleteUserAddress)
     139            1 : 
     140            1 :                 // 学歴関連情報
     141            1 :                 educationG := freelanceG.Group("/education")
     142            1 :                 educationG.GET("/", hEducation.ListEducation)
     143            1 :                 educationG.GET("/exists", hEducation.ExistsEducation)
     144            1 :                 educationG.POST("/", hEducation.AddEducation)
     145            1 :                 educationG.PATCH("/:education_id", hEducation.UpdateEducation)
     146            1 :                 educationG.DELETE("/:education_id", hEducation.DeleteEducation)
     147            1 :                 educationG.PATCH("/order", hEducation.OrderEducation)
     148            1 : 
     149            1 :                 // スキル関連
     150            1 :                 skillG := freelanceG.Group("/skill")
     151            1 :                 skillG.GET("/", hSkill.ListSkill)
     152            1 :                 skillG.GET("/exists", hSkill.ExistsSkill)
     153            1 :                 skillG.POST("/suggest", hSkill.SuggestSkill)
     154            1 :                 skillG.POST("/", hSkill.AddSkill)
     155            1 :                 skillG.PATCH("/:skill_id", hSkill.EditSkill)
     156            1 :                 skillG.DELETE("/:skill_id", hSkill.DeleteSkill)
     157            1 :                 skillG.PATCH("/order", hSkill.OrderSkill)
     158            1 : 
     159            1 :                 // 連絡先関連
     160            1 :                 contactG := freelanceG.Group("/contact")
     161            1 :                 contactG.GET("/", hContact.ListContact)
     162            1 :                 contactG.GET("/exists", hContact.ExistsContact)
     163            1 :                 contactG.POST("/", hContact.AddContact)
     164            1 :                 contactG.PATCH("/:contact_id", hContact.EditContact)
     165            1 :                 contactG.DELETE("/:contact_id", hContact.DeleteContact)
     166            1 :                 contactG.PATCH("/reorder", hContact.ReorderContact)
     167            1 : 
     168            1 :                 // 資格・免許関連
     169            1 :                 licenseG := freelanceG.Group("/license")
     170            1 :                 licenseG.GET("/", hLicense.ListLicense)
     171            1 :                 licenseG.GET("/exists", hLicense.ExistsLicense)
     172            1 :                 licenseG.POST("/", hLicense.AddLicense)
     173            1 :                 licenseG.GET("/:license_id", hLicense.DetailLicense)
     174            1 :                 licenseG.PATCH("/:license_id", hLicense.EditLicense)
     175            1 :                 licenseG.DELETE("/:license_id", hLicense.DeleteLicense)
     176            1 :                 licenseG.PATCH("/reorder", hLicense.ReorderLicense)
     177            1 : 
     178            1 :         }
     179              :         // endregion firebase認可
     180              : 
     181              :         // region agent、corporate、adminが使うユーザー情報
     182            1 :         userGroup := r.Group("/users")
     183            2 :         {
     184            1 :                 userGroup.GET("/:id", hUser.GetByID)
     185            1 :         }
     186              :         // endregion agent、corporate、adminが使うユーザー情報
     187              : 
     188              :         // region 認証とかロールとか無関係に誰でも使うやつを置く
     189            1 :         optionG := r.Group("/option")
     190            2 :         {
     191            1 :                 optionAddressG := optionG.Group("/address")
     192            2 :                 {
     193            1 :                         // 目的
     194            1 :                         optionAddressG.GET("/purpose", hMeta.ListAddressPurpose)
     195            1 :                         // 国コード
     196            1 :                         optionAddressG.GET("/countries", hMeta.ListCountry)
     197            1 :                 }
     198              :                 // 性別
     199            1 :                 optionG.GET("/gender", hMeta.ListGender)
     200            1 : 
     201            1 :                 optionEducationG := optionG.Group("/education")
     202            2 :                 {
     203            1 :                         // 学歴状態
     204            1 :                         optionEducationG.GET("/status", hMeta.ListEducationStatus)
     205            1 :                         // 学位種別
     206            1 :                         optionEducationG.GET("/degree", hMeta.ListDegreeType)
     207            1 :                 }
     208              : 
     209              :                 // スキルに関するオプション取得
     210            1 :                 optionSkillG := optionG.Group("/skill")
     211            2 :                 {
     212            1 :                         // スキルカテゴリ一覧
     213            1 :                         optionSkillG.GET("/category", hMeta.ListSkillCategory)
     214            1 :                         // スキルレベル一覧
     215            1 :                         optionSkillG.GET("/level", hMeta.ListSkillLevel)
     216            1 :                 }
     217              : 
     218              :                 // 職域に関するオプション値取得
     219            1 :                 occupationG := optionG.Group("/occupation")
     220            2 :                 {
     221            1 :                         occupationG.GET("/category", hMeta.ListOccupation)
     222            1 :                 }
     223              : 
     224              :                 // 経歴に関するオプション値取得
     225            1 :                 experienceG := optionG.Group("/experience")
     226            2 :                 {
     227            1 :                         experienceG.GET("/", hMeta.ListExperience)
     228            1 :                 }
     229              : 
     230              :                 // 続柄に関するオプション値取得
     231            1 :                 relationshipG := optionG.Group("/relationship")
     232            2 :                 {
     233            1 :                         relationshipG.GET("/", hMeta.ListRelationshipType)
     234            1 :                 }
     235              :         }
     236              :         // endregion 認証とかロールとか無関係に誰でも使うやつを置く
     237              : 
     238            1 :         return r
     239              : }
        

Generated by: LCOV version 2.3.1-1