@nakedev/go-scaffold 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +288 -50
- package/dist/commands/auth.js +53 -22
- package/dist/commands/config.js +50 -0
- package/dist/commands/create.js +32 -2
- package/dist/commands/generate.js +25 -2
- package/dist/commands/method.js +22 -7
- package/dist/commands/migration.js +2 -2
- package/dist/commands/observability.js +3 -3
- package/dist/commands/rbac.js +3 -3
- package/dist/commands/undo.js +5 -0
- package/dist/commands/worker.js +1 -1
- package/dist/index.js +186 -59
- package/dist/prompts/auth-wizard.js +40 -6
- package/dist/prompts/create-wizard.js +42 -1
- package/dist/prompts/generate-wizard.js +89 -9
- package/dist/templates/auth-manifest.js +31 -1
- package/dist/templates/create-manifest.js +4 -0
- package/dist/templates/module-manifest.js +37 -1
- package/dist/templates/rbac-manifest.js +1 -0
- package/dist/types.js +6 -0
- package/dist/utils/auth-patcher.js +115 -24
- package/dist/utils/config.js +147 -3
- package/dist/utils/main-patcher.js +29 -27
- package/dist/utils/marker-patch.js +7 -1
- package/dist/utils/method-patcher.js +261 -81
- package/dist/utils/module-profile.js +32 -0
- package/dist/utils/platform-patcher.js +29 -7
- package/dist/utils/rbac-patcher.js +97 -75
- package/package.json +7 -2
- package/templates/add/auth/cmd/seed/main.go.hbs +13 -3
- package/templates/add/auth/docs/login.yaml.hbs +11 -1
- package/templates/add/auth/docs/mfa-verify.yaml.hbs +19 -0
- package/templates/add/auth/docs/provider-exchange.yaml.hbs +40 -0
- package/templates/add/auth/docs/provider-login.yaml.hbs +31 -0
- package/templates/add/auth/docs/refresh.yaml.hbs +7 -0
- package/templates/add/auth/docs/register.yaml.hbs +7 -0
- package/templates/add/auth/docs/reset-password.yaml.hbs +1 -1
- package/templates/add/auth/docs/schemas.yaml.hbs +59 -1
- package/templates/add/auth/docs/users-me-mfa-confirm.yaml.hbs +19 -0
- package/templates/add/auth/docs/users-me-mfa-disable.yaml.hbs +15 -0
- package/templates/add/auth/docs/users-me-mfa-setup.yaml.hbs +14 -0
- package/templates/add/auth/docs/users-me-mfa.yaml.hbs +12 -0
- package/templates/add/auth/internal/app/user/application/oauth.go.hbs +132 -0
- package/templates/add/auth/internal/app/user/application/recovery.go.hbs +113 -0
- package/templates/add/auth/internal/app/user/browser_policy.go.hbs +98 -0
- package/templates/add/auth/internal/app/user/composition.go.hbs +165 -0
- package/templates/add/auth/internal/app/user/contracts.go.hbs +88 -0
- package/templates/add/auth/internal/app/user/dto.go.hbs +57 -0
- package/templates/add/auth/internal/app/user/errors.go.hbs +25 -0
- package/templates/add/auth/internal/app/user/external_login.go.hbs +208 -0
- package/templates/add/auth/internal/app/user/handler.go.hbs +60 -203
- package/templates/add/auth/internal/app/user/handler_local.go.hbs +75 -0
- package/templates/add/auth/internal/app/user/handler_mfa.go.hbs +83 -0
- package/templates/add/auth/internal/app/user/handler_oauth.go.hbs +70 -0
- package/templates/add/auth/internal/app/user/handler_recovery.go.hbs +49 -0
- package/templates/add/auth/internal/app/user/handler_test.go.hbs +290 -0
- package/templates/add/auth/internal/app/user/handler_user.go.hbs +41 -0
- package/templates/add/auth/internal/app/user/jwt.go.hbs +6 -59
- package/templates/add/auth/internal/app/user/local_auth.go.hbs +98 -0
- package/templates/add/auth/internal/app/user/mfa_service.go.hbs +450 -0
- package/templates/add/auth/internal/app/user/mfa_service_test.go.hbs +199 -0
- package/templates/add/auth/internal/app/user/mfa_store.go.hbs +127 -0
- package/templates/add/auth/internal/app/user/mfa_store_test.go.hbs +174 -0
- package/templates/add/auth/internal/app/user/model/authtoken.go.hbs +8 -2
- package/templates/add/auth/internal/app/user/model/identity.go.hbs +4 -3
- package/templates/add/auth/internal/app/user/model/mfa_challenge.go.hbs +17 -0
- package/templates/add/auth/internal/app/user/model/mfa_enrollment.go.hbs +20 -0
- package/templates/add/auth/internal/app/user/model/mfa_recovery_code.go.hbs +17 -0
- package/templates/add/auth/internal/app/user/model/user.go.hbs +3 -2
- package/templates/add/auth/internal/app/user/provider_test.go.hbs +286 -0
- package/templates/add/auth/internal/app/user/recovery_service.go.hbs +114 -0
- package/templates/add/auth/internal/app/user/repository.go.hbs +2 -0
- package/templates/add/auth/internal/app/user/service.go.hbs +82 -478
- package/templates/add/auth/internal/app/user/service_test.go.hbs +601 -45
- package/templates/add/auth/internal/app/user/session_cookie.go.hbs +33 -0
- package/templates/add/auth/internal/app/user/sessions.go.hbs +99 -0
- package/templates/add/auth/internal/app/user/tokenstore.go.hbs +42 -14
- package/templates/add/auth/internal/app/user/tokenstore_pg.go.hbs +105 -40
- package/templates/add/auth/internal/app/user/tokenstore_pg_test.go.hbs +96 -0
- package/templates/add/auth/internal/app/user/tokenstore_recovery.go.hbs +58 -0
- package/templates/add/auth/internal/app/user/tokenstore_redis.go.hbs +144 -70
- package/templates/add/auth/internal/app/user/tokenstore_redis_test.go.hbs +185 -0
- package/templates/add/auth/internal/app/user/user_query.go.hbs +65 -0
- package/templates/add/auth/internal/platform/authprovider/google/google.go.hbs +389 -0
- package/templates/add/auth/internal/platform/authprovider/google/google_test.go.hbs +312 -0
- package/templates/add/auth/migrations/create_auth_tokens.up.sql.hbs +9 -4
- package/templates/add/auth/migrations/create_identities.up.sql.hbs +1 -1
- package/templates/add/auth/migrations/create_mfa.down.sql.hbs +3 -0
- package/templates/add/auth/migrations/create_mfa.up.sql.hbs +29 -0
- package/templates/add/auth/migrations/create_users.up.sql.hbs +2 -2
- package/templates/add/rbac/internal/app/role/composition.go.hbs +35 -0
- package/templates/add/rbac/internal/app/role/service.go.hbs +12 -12
- package/templates/create/base/.claude/skills/go-scaffold/SKILL.md.hbs +340 -121
- package/templates/create/base/.env.example.hbs +0 -1
- package/templates/create/base/AGENTS.md.hbs +255 -67
- package/templates/create/base/Makefile.hbs +2 -1
- package/templates/create/base/README.md.hbs +42 -14
- package/templates/create/base/cmd/api/wiring.go.hbs +11 -8
- package/templates/create/base/internal/platform/database/database.go.hbs +3 -3
- package/templates/create/base/internal/shared/apperror/apperror.go.hbs +15 -2
- package/templates/create/base/internal/shared/config/config.go.hbs +0 -8
- package/templates/create/base/internal/shared/middleware/cors_test.go.hbs +40 -0
- package/templates/create/base/internal/shared/middleware/error.go.hbs +15 -5
- package/templates/create/features/docs/architecture.md.hbs +35 -13
- package/templates/create/features/docs/patterns.md.hbs +40 -21
- package/templates/create/features/docs/techstack.md.hbs +2 -2
- package/templates/generate/module/commands.go.hbs +95 -0
- package/templates/generate/module/composition.go.hbs +23 -0
- package/templates/generate/module/cqrs_test.go.hbs +7 -0
- package/templates/generate/module/handler.go.hbs +50 -5
- package/templates/generate/module/minimal/commands.go.hbs +34 -0
- package/templates/generate/module/minimal/handler.go.hbs +34 -0
- package/templates/generate/module/minimal/queries.go.hbs +45 -0
- package/templates/generate/module/minimal/service.go.hbs +27 -1
- package/templates/generate/module/queries.go.hbs +62 -0
- package/templates/generate/module/service.go.hbs +61 -5
- package/templates/add/auth/docs/google-callback.yaml.hbs +0 -22
- package/templates/add/auth/docs/google-login.yaml.hbs +0 -7
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
package {{pkg}}
|
|
2
2
|
|
|
3
|
+
{{#if cqrs}}
|
|
4
|
+
// repository is the complete data dependency retained for the compatibility
|
|
5
|
+
// facade below. CQRS handlers consume the narrower command/query ports.
|
|
6
|
+
type repository interface {
|
|
7
|
+
commandRepository
|
|
8
|
+
queryRepository
|
|
9
|
+
// go-scaffold:repository-interface
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Service is a compatibility facade. New code should depend on
|
|
13
|
+
// commandService or queryService directly.
|
|
14
|
+
type Service struct {
|
|
15
|
+
commands commandService
|
|
16
|
+
queries queryService
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
func NewService(repo repository) *Service {
|
|
20
|
+
return &Service{
|
|
21
|
+
commands: NewCommandHandler(repo),
|
|
22
|
+
queries: NewQueryHandler(repo),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// go-scaffold:service-methods
|
|
27
|
+
{{else}}
|
|
3
28
|
import (
|
|
4
29
|
"context"
|
|
5
30
|
"errors"
|
|
@@ -39,7 +64,8 @@ func wrapFindErr(err error) error {
|
|
|
39
64
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
40
65
|
return errNotFound()
|
|
41
66
|
}
|
|
42
|
-
return apperror.NewInternal()
|
|
67
|
+
return apperror.NewInternal(err)
|
|
43
68
|
}
|
|
44
69
|
|
|
45
70
|
// go-scaffold:service-methods
|
|
71
|
+
{{/if}}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package {{pkg}}
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"errors"
|
|
6
|
+
|
|
7
|
+
"{{goModule}}/internal/app/{{modulePath}}/model"
|
|
8
|
+
"{{goModule}}/internal/shared/apperror"
|
|
9
|
+
|
|
10
|
+
"github.com/google/uuid"
|
|
11
|
+
"gorm.io/gorm"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// queryRepository is the outbound port for read-only use cases. A separate
|
|
15
|
+
// port makes it possible to replace this with a projection/read model later
|
|
16
|
+
// without changing command handlers.
|
|
17
|
+
type queryRepository interface {
|
|
18
|
+
FindAll(ctx context.Context, limit, offset int) ([]model.{{pascalName}}, error)
|
|
19
|
+
FindByID(ctx context.Context, id uuid.UUID) (*model.{{pascalName}}, error)
|
|
20
|
+
// go-scaffold:query-repository-interface
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// queryService is the inbound application port consumed by read endpoints.
|
|
24
|
+
type queryService interface {
|
|
25
|
+
List(context.Context, int, int) ([]model.{{pascalName}}, error)
|
|
26
|
+
Get(context.Context, uuid.UUID) (*model.{{pascalName}}, error)
|
|
27
|
+
// go-scaffold:query-interface
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// QueryHandler owns read-only use cases for {{pkg}}.
|
|
31
|
+
type QueryHandler struct {
|
|
32
|
+
repo queryRepository
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func NewQueryHandler(repo queryRepository) *QueryHandler {
|
|
36
|
+
return &QueryHandler{repo: repo}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func (h *QueryHandler) List(ctx context.Context, limit, offset int) ([]model.{{pascalName}}, error) {
|
|
40
|
+
items, err := h.repo.FindAll(ctx, limit, offset)
|
|
41
|
+
if err != nil {
|
|
42
|
+
return nil, apperror.NewInternal(err)
|
|
43
|
+
}
|
|
44
|
+
return items, nil
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (h *QueryHandler) Get(ctx context.Context, id uuid.UUID) (*model.{{pascalName}}, error) {
|
|
48
|
+
m, err := h.repo.FindByID(ctx, id)
|
|
49
|
+
if err != nil {
|
|
50
|
+
return nil, wrapFindErr(err)
|
|
51
|
+
}
|
|
52
|
+
return m, nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func wrapFindErr(err error) error {
|
|
56
|
+
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
57
|
+
return errNotFound()
|
|
58
|
+
}
|
|
59
|
+
return apperror.NewInternal(err)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// go-scaffold:query-methods
|
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
package {{pkg}}
|
|
2
2
|
|
|
3
|
+
{{#if cqrs}}
|
|
4
|
+
import (
|
|
5
|
+
"context"
|
|
6
|
+
|
|
7
|
+
"{{goModule}}/internal/app/{{modulePath}}/model"
|
|
8
|
+
|
|
9
|
+
"github.com/google/uuid"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// repository is the complete data dependency retained for the compatibility
|
|
13
|
+
// facade below. The actual CQRS handlers consume the narrower
|
|
14
|
+
// commandRepository/queryRepository ports declared in commands.go/queries.go.
|
|
15
|
+
type repository interface {
|
|
16
|
+
commandRepository
|
|
17
|
+
queryRepository
|
|
18
|
+
// go-scaffold:repository-interface
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Service is a compatibility facade for callers that still want one feature
|
|
22
|
+
// service. New code should depend on commandService or queryService instead,
|
|
23
|
+
// so the read and write paths remain independently replaceable.
|
|
24
|
+
type Service struct {
|
|
25
|
+
commands commandService
|
|
26
|
+
queries queryService
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func NewService(repo repository) *Service {
|
|
30
|
+
return &Service{
|
|
31
|
+
commands: NewCommandHandler(repo),
|
|
32
|
+
queries: NewQueryHandler(repo),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func (s *Service) Create(ctx context.Context, in createInput) (*model.{{pascalName}}, error) {
|
|
37
|
+
return s.commands.Create(ctx, in)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func (s *Service) List(ctx context.Context, limit, offset int) ([]model.{{pascalName}}, error) {
|
|
41
|
+
return s.queries.List(ctx, limit, offset)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func (s *Service) Get(ctx context.Context, id uuid.UUID) (*model.{{pascalName}}, error) {
|
|
45
|
+
return s.queries.Get(ctx, id)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func (s *Service) Update(ctx context.Context, id uuid.UUID, in updateInput) (*model.{{pascalName}}, error) {
|
|
49
|
+
return s.commands.Update(ctx, id, in)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
|
|
53
|
+
return s.commands.Delete(ctx, id)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// go-scaffold:service-methods
|
|
57
|
+
{{else}}
|
|
3
58
|
import (
|
|
4
59
|
"context"
|
|
5
60
|
"errors"
|
|
@@ -41,7 +96,7 @@ func (s *Service) Create(ctx context.Context, in createInput) (*model.{{pascalNa
|
|
|
41
96
|
if dberr.IsDuplicate(err) {
|
|
42
97
|
return nil, errConflict()
|
|
43
98
|
}
|
|
44
|
-
return nil, apperror.NewInternal()
|
|
99
|
+
return nil, apperror.NewInternal(err)
|
|
45
100
|
}
|
|
46
101
|
return m, nil
|
|
47
102
|
}
|
|
@@ -49,7 +104,7 @@ func (s *Service) Create(ctx context.Context, in createInput) (*model.{{pascalNa
|
|
|
49
104
|
func (s *Service) List(ctx context.Context, limit, offset int) ([]model.{{pascalName}}, error) {
|
|
50
105
|
items, err := s.repo.FindAll(ctx, limit, offset)
|
|
51
106
|
if err != nil {
|
|
52
|
-
return nil, apperror.NewInternal()
|
|
107
|
+
return nil, apperror.NewInternal(err)
|
|
53
108
|
}
|
|
54
109
|
return items, nil
|
|
55
110
|
}
|
|
@@ -79,7 +134,7 @@ func (s *Service) Update(ctx context.Context, id uuid.UUID, in updateInput) (*mo
|
|
|
79
134
|
if dberr.IsDuplicate(err) {
|
|
80
135
|
return nil, errConflict()
|
|
81
136
|
}
|
|
82
|
-
return nil, apperror.NewInternal()
|
|
137
|
+
return nil, apperror.NewInternal(err)
|
|
83
138
|
}
|
|
84
139
|
return m, nil
|
|
85
140
|
}
|
|
@@ -93,7 +148,7 @@ func (s *Service) Delete(ctx context.Context, id uuid.UUID) error {
|
|
|
93
148
|
if dberr.IsForeignKey(err) {
|
|
94
149
|
return errHasReferences()
|
|
95
150
|
}
|
|
96
|
-
return apperror.NewInternal()
|
|
151
|
+
return apperror.NewInternal(err)
|
|
97
152
|
}
|
|
98
153
|
return nil
|
|
99
154
|
}
|
|
@@ -102,7 +157,8 @@ func wrapFindErr(err error) error {
|
|
|
102
157
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
103
158
|
return errNotFound()
|
|
104
159
|
}
|
|
105
|
-
return apperror.NewInternal()
|
|
160
|
+
return apperror.NewInternal(err)
|
|
106
161
|
}
|
|
107
162
|
|
|
108
163
|
// go-scaffold:service-methods
|
|
164
|
+
{{/if}}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
get:
|
|
2
|
-
summary: Google OAuth callback
|
|
3
|
-
description: Redirect target Google sends the browser back to after consent — not called directly by API clients.
|
|
4
|
-
operationId: googleCallback
|
|
5
|
-
tags: [auth]
|
|
6
|
-
security: []
|
|
7
|
-
parameters:
|
|
8
|
-
- name: code
|
|
9
|
-
in: query
|
|
10
|
-
required: true
|
|
11
|
-
schema: { type: string }
|
|
12
|
-
- name: state
|
|
13
|
-
in: query
|
|
14
|
-
required: true
|
|
15
|
-
schema: { type: string }
|
|
16
|
-
responses:
|
|
17
|
-
"200":
|
|
18
|
-
description: ok, refresh_token set as an httpOnly cookie
|
|
19
|
-
content:
|
|
20
|
-
application/json:
|
|
21
|
-
schema: { $ref: './schemas.yaml#/AuthResponse' }
|
|
22
|
-
"401": { $ref: '../common/responses.yaml#/UnauthorizedError' }
|