You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
330 lines
7.9 KiB
330 lines
7.9 KiB
|
4 weeks ago
|
package service
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
"teraclone/internal/cli"
|
||
|
|
"teraclone/internal/store"
|
||
|
|
)
|
||
|
|
|
||
|
|
type AppService struct {
|
||
|
|
store *store.SQLiteStore
|
||
|
|
}
|
||
|
|
|
||
|
|
type User struct {
|
||
|
|
ID int64 `json:"id"`
|
||
|
|
Username string `json:"username"`
|
||
|
|
Group string `json:"group"`
|
||
|
|
Enabled bool `json:"enabled"`
|
||
|
|
StatusLabel string `json:"statusLabel"`
|
||
|
|
AccessSummary string `json:"accessSummary"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type CreateUserInput struct {
|
||
|
|
Username string `json:"username"`
|
||
|
|
Password string `json:"password"`
|
||
|
|
Group string `json:"group"`
|
||
|
|
Enabled bool `json:"enabled"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type UpdateUserInput struct {
|
||
|
|
ID int64 `json:"id"`
|
||
|
|
Username string `json:"username"`
|
||
|
|
Password string `json:"password"`
|
||
|
|
Group string `json:"group"`
|
||
|
|
Enabled bool `json:"enabled"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Group struct {
|
||
|
|
ID int64 `json:"id"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
Permissions []string `json:"permissions"`
|
||
|
|
PermissionSummary string `json:"permissionSummary"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type CreateGroupInput struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
Permissions []string `json:"permissions"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type UpdateGroupInput struct {
|
||
|
|
ID int64 `json:"id"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
Permissions []string `json:"permissions"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewAppService(dbPath string) (*AppService, error) {
|
||
|
|
sqliteStore, err := store.NewSQLiteStore(dbPath)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return &AppService{store: sqliteStore}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) Greeting() string {
|
||
|
|
return "hello from go server"
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) HealthStatus() string {
|
||
|
|
return "ok"
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) NewTerminalSession() (*cli.TerminalSession, error) {
|
||
|
|
return cli.NewTerminalSession()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) Close() error {
|
||
|
|
if s == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return s.store.Close()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) RecordPageVisit(path string, title string) error {
|
||
|
|
return s.store.RecordPageVisit(path, title)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) RecordMockAction(action string, path string) error {
|
||
|
|
return s.store.RecordMockAction(action, path)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) RecentActivity(limit int) ([]store.ActivityRecord, error) {
|
||
|
|
return s.store.RecentActivity(limit)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) ListUsers() ([]User, error) {
|
||
|
|
records, err := s.store.ListUsers()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
users := make([]User, 0, len(records))
|
||
|
|
for _, record := range records {
|
||
|
|
users = append(users, mapUser(record))
|
||
|
|
}
|
||
|
|
return users, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) ListGroups() ([]Group, error) {
|
||
|
|
records, err := s.store.ListGroups()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
groups := make([]Group, 0, len(records))
|
||
|
|
for _, record := range records {
|
||
|
|
groups = append(groups, mapGroup(record))
|
||
|
|
}
|
||
|
|
return groups, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) GetGroup(id int64) (Group, error) {
|
||
|
|
record, err := s.store.GetGroup(id)
|
||
|
|
if err != nil {
|
||
|
|
return Group{}, err
|
||
|
|
}
|
||
|
|
return mapGroup(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) CreateGroup(input CreateGroupInput) (Group, error) {
|
||
|
|
if err := validateGroupInput(input.Name, input.Permissions); err != nil {
|
||
|
|
return Group{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
record, err := s.store.CreateGroup(store.CreateGroupParams{
|
||
|
|
Name: input.Name,
|
||
|
|
Description: input.Description,
|
||
|
|
Permissions: input.Permissions,
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return Group{}, err
|
||
|
|
}
|
||
|
|
return mapGroup(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) UpdateGroup(input UpdateGroupInput) (Group, error) {
|
||
|
|
if input.ID <= 0 {
|
||
|
|
return Group{}, errors.New("invalid group id")
|
||
|
|
}
|
||
|
|
if err := validateGroupInput(input.Name, input.Permissions); err != nil {
|
||
|
|
return Group{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
record, err := s.store.UpdateGroup(store.UpdateGroupParams{
|
||
|
|
ID: input.ID,
|
||
|
|
Name: input.Name,
|
||
|
|
Description: input.Description,
|
||
|
|
Permissions: input.Permissions,
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return Group{}, err
|
||
|
|
}
|
||
|
|
return mapGroup(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) DeleteGroup(id int64) error {
|
||
|
|
if id <= 0 {
|
||
|
|
return errors.New("invalid group id")
|
||
|
|
}
|
||
|
|
return s.store.DeleteGroup(id)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) GetUser(id int64) (User, error) {
|
||
|
|
record, err := s.store.GetUser(id)
|
||
|
|
if err != nil {
|
||
|
|
return User{}, err
|
||
|
|
}
|
||
|
|
return mapUser(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) CreateUser(input CreateUserInput) (User, error) {
|
||
|
|
group, err := s.store.GetGroupByName(strings.TrimSpace(input.Group))
|
||
|
|
if err != nil {
|
||
|
|
return User{}, errors.New("group not found")
|
||
|
|
}
|
||
|
|
if err := validateUserInput(input.Username, input.Password, input.Group, true); err != nil {
|
||
|
|
return User{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
record, err := s.store.CreateUser(store.CreateUserParams{
|
||
|
|
Username: input.Username,
|
||
|
|
Password: input.Password,
|
||
|
|
GroupName: group.Name,
|
||
|
|
Enabled: input.Enabled,
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return User{}, err
|
||
|
|
}
|
||
|
|
return mapUser(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) UpdateUser(input UpdateUserInput) (User, error) {
|
||
|
|
if input.ID <= 0 {
|
||
|
|
return User{}, errors.New("invalid user id")
|
||
|
|
}
|
||
|
|
group, err := s.store.GetGroupByName(strings.TrimSpace(input.Group))
|
||
|
|
if err != nil {
|
||
|
|
return User{}, errors.New("group not found")
|
||
|
|
}
|
||
|
|
if err := validateUserInput(input.Username, input.Password, input.Group, false); err != nil {
|
||
|
|
return User{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
record, err := s.store.UpdateUser(store.UpdateUserParams{
|
||
|
|
ID: input.ID,
|
||
|
|
Username: input.Username,
|
||
|
|
Password: input.Password,
|
||
|
|
GroupName: group.Name,
|
||
|
|
Enabled: input.Enabled,
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return User{}, err
|
||
|
|
}
|
||
|
|
return mapUser(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) DeleteUser(id int64) error {
|
||
|
|
if id <= 0 {
|
||
|
|
return errors.New("invalid user id")
|
||
|
|
}
|
||
|
|
return s.store.DeleteUser(id)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *AppService) AuthenticateUser(username string, password string) (User, error) {
|
||
|
|
record, err := s.store.GetUserByUsername(strings.TrimSpace(username))
|
||
|
|
if err != nil {
|
||
|
|
return User{}, err
|
||
|
|
}
|
||
|
|
if !record.Enabled {
|
||
|
|
return User{}, errors.New("user is disabled")
|
||
|
|
}
|
||
|
|
if strings.TrimSpace(password) == "" || !store.VerifyPassword(password, record.PasswordHash) {
|
||
|
|
return User{}, errors.New("invalid username or password")
|
||
|
|
}
|
||
|
|
return mapUser(record), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func mapUser(record store.UserRecord) User {
|
||
|
|
return User{
|
||
|
|
ID: record.ID,
|
||
|
|
Username: record.Username,
|
||
|
|
Group: record.GroupName,
|
||
|
|
Enabled: record.Enabled,
|
||
|
|
StatusLabel: statusLabel(record.Enabled),
|
||
|
|
AccessSummary: accessSummary(record.GroupName),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func mapGroup(record store.GroupRecord) Group {
|
||
|
|
return Group{
|
||
|
|
ID: record.ID,
|
||
|
|
Name: record.Name,
|
||
|
|
Description: record.Description,
|
||
|
|
Permissions: record.Permissions,
|
||
|
|
PermissionSummary: permissionSummary(record.Permissions),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateUserInput(username string, password string, group string, passwordRequired bool) error {
|
||
|
|
if strings.TrimSpace(username) == "" {
|
||
|
|
return errors.New("username is required")
|
||
|
|
}
|
||
|
|
if passwordRequired && strings.TrimSpace(password) == "" {
|
||
|
|
return errors.New("password is required")
|
||
|
|
}
|
||
|
|
if strings.TrimSpace(group) == "" {
|
||
|
|
return errors.New("group is required")
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func statusLabel(enabled bool) string {
|
||
|
|
if enabled {
|
||
|
|
return "사용"
|
||
|
|
}
|
||
|
|
return "중지"
|
||
|
|
}
|
||
|
|
|
||
|
|
func accessSummary(group string) string {
|
||
|
|
switch strings.TrimSpace(group) {
|
||
|
|
case "administrator":
|
||
|
|
return "전체 허용"
|
||
|
|
case "operator":
|
||
|
|
return "운영 허용"
|
||
|
|
case "guest":
|
||
|
|
return "조회 허용"
|
||
|
|
default:
|
||
|
|
return "제한됨"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateGroupInput(name string, permissions []string) error {
|
||
|
|
if strings.TrimSpace(name) == "" {
|
||
|
|
return errors.New("group name is required")
|
||
|
|
}
|
||
|
|
if len(permissions) == 0 {
|
||
|
|
return errors.New("at least one permission is required")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func permissionSummary(permissions []string) string {
|
||
|
|
if len(permissions) == 0 {
|
||
|
|
return "권한 없음"
|
||
|
|
}
|
||
|
|
if len(permissions) == len(defaultPermissionOptions()) {
|
||
|
|
return "모든 메뉴"
|
||
|
|
}
|
||
|
|
return fmt.Sprintf("권한 %d개", len(permissions))
|
||
|
|
}
|
||
|
|
|
||
|
|
func defaultPermissionOptions() []string {
|
||
|
|
return []string{"login", "network", "ports", "tools", "admin", "common", "status", "password", "operate"}
|
||
|
|
}
|