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.
 
 
 
 
 
 

329 lines
7.9 KiB

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"}
}