修正 .gitignore:alterminal 模式誤忽略 cmd/alterminal,補提交 main package

This commit is contained in:
2026-10-03 21:10:13 +08:00
parent 5fc1755c17
commit f4c5bc5823
2 changed files with 107 additions and 2 deletions
+104
View File
@@ -0,0 +1,104 @@
package main
import (
"log"
"net/http"
"os"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"alterminal/internal/admin"
"alterminal/internal/auth"
"alterminal/internal/cli"
"alterminal/internal/oidc"
"alterminal/internal/store"
)
func main() {
if len(os.Args) > 1 {
cli.Run(os.Args[1:])
return
}
db, err := store.Open()
if err != nil {
log.Fatal("database: ", err)
}
// issuer 須為含 scheme 的絕對 URL 且不含尾斜線(OIDC Discovery 1.0
// §3:issuer 字串與簽入 token 的 iss claim 必須完全一致),統一去除
// 尾斜線以容錯。
issuer := strings.TrimSuffix(store.EnvOr("ISSUER", "http://localhost:8080"), "/")
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Get("/", auth.AccountPageHandler(db))
r.Get("/users/{name}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, " + chi.URLParam(r, "name") + "!"))
})
r.Get("/.well-known/openid-configuration", oidc.DiscoveryHandler(issuer))
r.Get("/.well-known/jwks.json", oidc.JWKSHandler(db))
r.Get("/login", auth.LoginPageHandler(db))
r.Post("/login", auth.LoginHandler(db))
// RP-Initiated Logout 1.0 §2:登出端點須支援 GET 與 POST。不帶 RP
// 參數的 POST(側欄登出表單、JSON API)由 handler 內部委由
// auth.LogoutHandler 處理,行為不變。
r.Get("/logout", oidc.LogoutHandler(db, issuer))
r.Post("/logout", oidc.LogoutHandler(db, issuer))
// 更新密碼(登入者自助變更):驗證目前密碼後於交易內更新並撤銷全部
// Session,再輪替目前瀏覽器的 Session(其他裝置立即登出)。
r.Get("/password", auth.PasswordPageHandler(db))
r.Post("/password", auth.PasswordChangeHandler(db))
// OIDC 授權碼流程(RFC 6749 §4.1.1 + OIDC Core §3.1)。
r.Get("/authorize", oidc.AuthorizeHandler(db))
r.Post("/authorize", oidc.AuthorizeHandler(db))
r.Post("/token", oidc.TokenHandler(db, issuer))
r.Get("/userinfo", oidc.UserInfoHandler(db, issuer))
r.Post("/userinfo", oidc.UserInfoHandler(db, issuer))
r.Get("/admin/keys", admin.KeysPageHandler(db))
r.Post("/admin/keys", admin.KeysCreateHandler(db))
r.Post("/admin/keys/{id}/retire", admin.KeysRetireHandler(db))
r.Get("/admin/applications", admin.ApplicationsPageHandler(db))
r.Get("/admin/applications/new", admin.ApplicationNewPageHandler(db))
r.Post("/admin/applications/new", admin.ApplicationsCreateHandler(db))
r.Get("/admin/applications/{id}", admin.ApplicationEditPageHandler(db))
r.Post("/admin/applications/{id}", admin.ApplicationUpdateHandler(db))
r.Post("/admin/applications/{id}/secret", admin.ApplicationsRotateSecretHandler(db))
r.Post("/admin/applications/{id}/delete", admin.ApplicationsDeleteHandler(db))
r.Handle("/static/*", auth.StaticHandler())
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
sqlDB, err := db.DB()
if err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("db: " + err.Error()))
return
}
if err := sqlDB.Ping(); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte("db: " + err.Error()))
return
}
w.Write([]byte("ok"))
})
// 未匹配任何路由的路徑(不分方法)輸出自訂 404 頁。
r.NotFound(auth.NotFoundHandler)
if err := http.ListenAndServe(":8080", r); err != nil {
log.Fatal(err)
}
}