@simplysm/sd-cli 14.0.88 → 14.0.90

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.
Files changed (82) hide show
  1. package/dist/commands/init/generators/client-common.d.ts.map +1 -1
  2. package/dist/commands/init/generators/client-common.js +6 -2
  3. package/dist/commands/init/generators/client-common.js.map +1 -1
  4. package/dist/commands/init/generators/client.d.ts.map +1 -1
  5. package/dist/commands/init/generators/client.js +13 -2
  6. package/dist/commands/init/generators/client.js.map +1 -1
  7. package/dist/commands/init/generators/common.d.ts.map +1 -1
  8. package/dist/commands/init/generators/common.js +16 -1
  9. package/dist/commands/init/generators/common.js.map +1 -1
  10. package/dist/commands/init/generators/server.d.ts.map +1 -1
  11. package/dist/commands/init/generators/server.js +9 -0
  12. package/dist/commands/init/generators/server.js.map +1 -1
  13. package/dist/commands/init/init.d.ts.map +1 -1
  14. package/dist/commands/init/init.js +11 -2
  15. package/dist/commands/init/init.js.map +1 -1
  16. package/dist/commands/init/normalize.d.ts.map +1 -1
  17. package/dist/commands/init/normalize.js +40 -4
  18. package/dist/commands/init/normalize.js.map +1 -1
  19. package/dist/commands/init/prompts.d.ts +1 -1
  20. package/dist/commands/init/prompts.d.ts.map +1 -1
  21. package/dist/commands/init/prompts.js +34 -3
  22. package/dist/commands/init/prompts.js.map +1 -1
  23. package/dist/commands/init/types.d.ts +16 -1
  24. package/dist/commands/init/types.d.ts.map +1 -1
  25. package/dist/commands/init/validate.d.ts.map +1 -1
  26. package/dist/commands/init/validate.js +3 -0
  27. package/dist/commands/init/validate.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/commands/init/generators/client-common.ts +18 -5
  30. package/src/commands/init/generators/client.ts +41 -2
  31. package/src/commands/init/generators/common.ts +56 -2
  32. package/src/commands/init/generators/server.ts +30 -0
  33. package/src/commands/init/init.ts +12 -2
  34. package/src/commands/init/normalize.ts +49 -4
  35. package/src/commands/init/prompts.ts +34 -3
  36. package/src/commands/init/templates/client/login-public/assets/logo-landscape.png +0 -0
  37. package/src/commands/init/templates/client/login-public/assets/logo.png +0 -0
  38. package/src/commands/init/templates/client/package.json.hbs +3 -2
  39. package/src/commands/init/templates/client/src/app/home/home.view.ts.hbs +137 -0
  40. package/src/commands/init/templates/client/src/app/home/main/main.view.ts.hbs +16 -0
  41. package/src/commands/init/templates/client/src/app/home/my-info/my-info.detail.ts.hbs +265 -0
  42. package/src/commands/init/templates/client/src/app/login/login.view.ts.hbs +144 -0
  43. package/src/commands/init/templates/client/src/app.root.ts.hbs +64 -0
  44. package/src/commands/init/templates/client/src/index.html.hbs +75 -1
  45. package/src/commands/init/templates/client/src/main.ts.hbs +147 -7
  46. package/src/commands/init/templates/client/src/modals/dev.modal.ts.hbs +63 -0
  47. package/src/commands/init/templates/client/src/routes.ts.hbs +29 -0
  48. package/src/commands/init/templates/client-common/package.json.hbs +1 -0
  49. package/src/commands/init/templates/client-common/src/index.ts.hbs +6 -2
  50. package/src/commands/init/templates/client-common/src/providers/app-auth.provider.ts.hbs +90 -0
  51. package/src/commands/init/templates/client-common/src/providers/{AppOrmProvider.ts.hbs → app-orm.provider.ts.hbs} +5 -11
  52. package/src/commands/init/templates/client-common/src/providers/app-service.provider.ts.hbs +68 -0
  53. package/src/commands/init/templates/client-common/src/providers/app-shared-data.provider.ts.hbs +100 -0
  54. package/src/commands/init/templates/common/package.json.hbs +2 -1
  55. package/src/commands/init/templates/common/src/app-structure.ts.hbs +26 -0
  56. package/src/commands/init/templates/common/src/auth-info-changed.event.ts.hbs +3 -0
  57. package/src/commands/init/templates/common/src/db/db-context.ts.hbs +20 -0
  58. package/src/commands/init/templates/common/src/db/system-data-log.ext.ts.hbs +138 -0
  59. package/src/commands/init/templates/common/src/db/tables/master/user-config.ts.hbs +15 -0
  60. package/src/commands/init/templates/common/src/db/tables/master/user.ts.hbs +20 -0
  61. package/src/commands/init/templates/common/src/db/tables/system/role-permission.ts.hbs +16 -0
  62. package/src/commands/init/templates/common/src/db/tables/system/role.ts.hbs +16 -0
  63. package/src/commands/init/templates/common/src/db/tables/system/system-data-log.ts.hbs +23 -0
  64. package/src/commands/init/templates/common/src/db/tables/system/system-log.ts.hbs +21 -0
  65. package/src/commands/init/templates/common/src/index.ts.hbs +14 -1
  66. package/src/commands/init/templates/server/package.json.hbs +7 -3
  67. package/src/commands/init/templates/server/public-dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  68. package/src/commands/init/templates/server/src/index.ts.hbs +4 -0
  69. package/src/commands/init/templates/server/src/main.ts.hbs +11 -1
  70. package/src/commands/init/templates/server/src/services/auth.service.ts.hbs +284 -0
  71. package/src/commands/init/templates/server/src/services/dev.service.ts.hbs +112 -0
  72. package/src/commands/init/templates/server/src/utils/orm.utils.ts.hbs +8 -0
  73. package/src/commands/init/templates/workspace-root/sd.config.ts.hbs +2 -2
  74. package/src/commands/init/types.ts +16 -1
  75. package/src/commands/init/validate.ts +6 -0
  76. package/tests/init/__snapshots__/render.spec.ts.snap +36 -9
  77. package/tests/init/normalize.spec.ts +95 -1
  78. package/tests/init/render.spec.ts +951 -10
  79. package/src/commands/init/templates/client/src/AppPage.ts.hbs +0 -18
  80. package/src/commands/init/templates/client/src/routes.ts +0 -3
  81. package/src/commands/init/templates/client-common/src/providers/AppServiceProvider.ts +0 -27
  82. package/src/commands/init/templates/common/src/DbContext.ts.hbs +0 -4
@@ -1,31 +1,171 @@
1
- import consola from "consola";
1
+ {{#if hasServer}}
2
+ import { enableProdMode, inject, provideAppInitializer } from "@angular/core";
3
+ {{else}}
2
4
  import { enableProdMode } from "@angular/core";
5
+ {{/if}}
3
6
  import { bootstrapApplication } from "@angular/platform-browser";
4
7
  import { provideHttpClient, withFetch } from "@angular/common/http";
5
8
  {{#if client.hasRouter}}
6
9
  import { provideRouter, withHashLocation } from "@angular/router";
7
10
  {{/if}}
8
- import { provideSdAngular } from "@simplysm/angular";
9
- import { AppPage } from "./AppPage";
11
+ import {
12
+ provideSdAngular,
13
+ {{#if hasDb}}
14
+ SdSharedDataProvider,
15
+ SdSystemLogProvider,
16
+ {{/if}}
17
+ {{#if hasAuth}}
18
+ SdSystemConfigProvider,
19
+ {{#if client.hasRouter}}
20
+ SdAppStructureProvider,
21
+ {{/if}}
22
+ {{/if}}
23
+ } from "@simplysm/angular";
24
+ import { createLogger{{#if hasDb}}, DateTime, json{{/if}} } from "@simplysm/core-common";
25
+ {{#if hasAuth}}
26
+ import { expr } from "@simplysm/orm-common";
27
+ {{/if}}
28
+ {{#if hasServer}}
29
+ import {
30
+ AppServiceProvider,
31
+ {{#if hasDb}}
32
+ AppOrmProvider,
33
+ AppSharedDataProvider,
34
+ {{/if}}
35
+ {{#if hasAuth}}
36
+ AppAuthProvider,
37
+ {{/if}}
38
+ } from "@{{workspaceName}}/client-common";
39
+ {{/if}}
40
+ {{#if hasAuth}}
41
+ {{#if client.hasRouter}}
42
+ import { {{client.appStructureName}} } from "@{{workspaceName}}/common";
43
+ {{/if}}
44
+ {{/if}}
45
+ import { AppRoot } from "./app.root";
10
46
  {{#if client.hasRouter}}
11
47
  import { routes } from "./routes";
12
48
  {{/if}}
13
49
  import "./styles.scss";
14
50
 
51
+ const CLIENT_NAME = "{{client.name}}";
52
+ const logger = createLogger(CLIENT_NAME);
53
+
15
54
  if (typeof ngDevMode !== "undefined" && !ngDevMode) {
16
55
  enableProdMode();
17
56
  } else {
18
- consola.log("dev mode");
57
+ logger.info("dev mode");
19
58
  }
20
59
 
21
- bootstrapApplication(AppPage, {
60
+ bootstrapApplication(AppRoot, {
22
61
  providers: [
23
62
  provideHttpClient(withFetch()),
24
63
  {{#if client.hasRouter}}
25
64
  provideRouter(routes, withHashLocation()),
26
65
  {{/if}}
27
- provideSdAngular({ clientName: "{{client.name}}" }),
66
+ provideSdAngular({ clientName: CLIENT_NAME }),
67
+ {{#if hasDb}}
68
+
69
+ { provide: SdSharedDataProvider, useExisting: AppSharedDataProvider },
70
+ {{/if}}
71
+ {{#if hasServer}}
72
+
73
+ //-- app-service 연결
74
+ provideAppInitializer(async () => {
75
+ await inject(AppServiceProvider).connectAsync();
76
+ }),
77
+ {{/if}}
78
+ {{#if hasAuth}}
79
+ {{#if client.hasRouter}}
80
+
81
+ //-- app-structure 초기화
82
+ provideAppInitializer(() => {
83
+ inject(SdAppStructureProvider).initialize({{client.appStructureName}});
84
+ }),
85
+ {{/if}}
86
+ {{/if}}
87
+ {{#if hasDb}}
88
+
89
+ //-- 로그 DB 연동
90
+ provideAppInitializer(() => {
91
+ const sdSystemLog = inject(SdSystemLogProvider);
92
+ const appOrm = inject(AppOrmProvider);
93
+ {{#if hasAuth}}
94
+ const appAuth = inject(AppAuthProvider);
95
+ {{/if}}
96
+
97
+ sdSystemLog.writeFn = async (severity: "error" | "warn" | "log", ...logs: any[]) => {
98
+ await appOrm.connectAsync(async (db) => {
99
+ await db.systemLog().insert([
100
+ {
101
+ dateTime: new DateTime(),
102
+ severity,
103
+ message: logs
104
+ .map((l) =>
105
+ typeof l === "string"
106
+ ? l
107
+ : l instanceof Error
108
+ ? (l.stack ?? l.message)
109
+ : json.stringify(l, { space: 2 }),
110
+ )
111
+ .join(" "),
112
+ clientName: CLIENT_NAME,
113
+ {{#if hasAuth}}
114
+ {{userEntityCamel}}Id: appAuth.authInfo()?.{{userEntityCamel}}Id,
115
+ {{/if}}
116
+ },
117
+ ]);
118
+ });
119
+ };
120
+ }),
121
+ {{/if}}
122
+ {{#if hasAuth}}
123
+
124
+ //-- 시스템 설정 DB 연동
125
+ provideAppInitializer(() => {
126
+ const sdSystemConfig = inject(SdSystemConfigProvider);
127
+ const appAuth = inject(AppAuthProvider);
128
+ const appOrm = inject(AppOrmProvider);
129
+
130
+ sdSystemConfig.fn = {
131
+ set: async (key: string, val: any) => {
132
+ const {{userEntityCamel}}Id = appAuth.authInfo()?.{{userEntityCamel}}Id;
133
+ if ({{userEntityCamel}}Id == null) return;
134
+
135
+ await appOrm.connectAsync(async (db) => {
136
+ await db
137
+ .{{userEntityCamel}}Config()
138
+ .where((item) => [expr.eq(item.{{userEntityCamel}}Id, {{userEntityCamel}}Id), expr.eq(item.code, key)])
139
+ .upsert(
140
+ () => ({
141
+ valueJson: json.stringify(val),
142
+ }),
143
+ (updateRecord) => ({
144
+ ...updateRecord,
145
+ {{userEntityCamel}}Id,
146
+ code: key,
147
+ }),
148
+ );
149
+ });
150
+ },
151
+ get: async (key: string) => {
152
+ const {{userEntityCamel}}Id = appAuth.authInfo()?.{{userEntityCamel}}Id;
153
+ if ({{userEntityCamel}}Id == null) return;
154
+
155
+ return appOrm.connectAsync(async (db) => {
156
+ const {{userEntityCamel}}Config = await db
157
+ .{{userEntityCamel}}Config()
158
+ .where((item) => [expr.eq(item.{{userEntityCamel}}Id, {{userEntityCamel}}Id), expr.eq(item.code, key)])
159
+ .single();
160
+ return {{userEntityCamel}}Config?.valueJson != null
161
+ ? json.parse({{userEntityCamel}}Config.valueJson)
162
+ : undefined;
163
+ });
164
+ },
165
+ };
166
+ }),
167
+ {{/if}}
28
168
  ],
29
169
  }).catch((err: unknown) => {
30
- consola.error(err);
170
+ logger.error("부트스트랩 실패", err);
31
171
  });
@@ -0,0 +1,63 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ inject,
5
+ output,
6
+ signal,
7
+ ViewEncapsulation,
8
+ } from "@angular/core";
9
+ import {
10
+ SdBusyContainer,
11
+ SdButton,
12
+ type SdModalContentDef,
13
+ SdToastProvider,
14
+ } from "@simplysm/angular";
15
+ import { NgIcon } from "@ng-icons/core";
16
+ import { tablerDatabase, tablerRefresh } from "@ng-icons/tabler-icons";
17
+ import { AppServiceProvider } from "@{{workspaceName}}/client-common";
18
+
19
+ @Component({
20
+ selector: "app-dev-modal",
21
+ changeDetection: ChangeDetectionStrategy.OnPush,
22
+ encapsulation: ViewEncapsulation.None,
23
+ standalone: true,
24
+ imports: [SdBusyContainer, SdButton, NgIcon],
25
+ template: `
26
+ <sd-busy-container [busy]="busyCount() > 0">
27
+ <div class="p-default flex-column gap-sm">
28
+ <div class="flex-row gap-sm">
29
+ <sd-button (click)="onInitDbButtonClick()"><ng-icon [svg]="tablerDatabase" /> DB 초기화</sd-button>
30
+ </div>
31
+ <div class="flex-row gap-sm">
32
+ <sd-button [theme]="'gray'" (click)="onRefreshButtonClick()"><ng-icon [svg]="tablerRefresh" /> 새로고침</sd-button>
33
+ </div>
34
+ </div>
35
+ </sd-busy-container>
36
+ `,
37
+ })
38
+ export class DevModal implements SdModalContentDef<void> {
39
+ private readonly _sdToast = inject(SdToastProvider);
40
+ private readonly _appService = inject(AppServiceProvider);
41
+
42
+ initialized = signal(true);
43
+ busyCount = signal(0);
44
+
45
+ close = output<void | undefined>();
46
+
47
+ async onInitDbButtonClick() {
48
+ if (this.busyCount() > 0) return;
49
+ this.busyCount.update((v) => v + 1);
50
+ await this._sdToast.try(async () => {
51
+ await this._appService.dev.initDb();
52
+ this._sdToast.success("DB 초기화가 완료되었습니다.");
53
+ });
54
+ this.busyCount.update((v) => v - 1);
55
+ }
56
+
57
+ onRefreshButtonClick() {
58
+ location.reload();
59
+ }
60
+
61
+ protected readonly tablerDatabase = tablerDatabase;
62
+ protected readonly tablerRefresh = tablerRefresh;
63
+ }
@@ -0,0 +1,29 @@
1
+ import type { Routes } from "@angular/router";
2
+
3
+ {{#if hasAuth}}
4
+ export const routes: Routes = [
5
+ { path: "", redirectTo: "/login", pathMatch: "full" },
6
+ {
7
+ path: "login",
8
+ loadComponent: () => import("./app/login/login.view").then((m) => m.LoginView),
9
+ },
10
+ {
11
+ path: "home",
12
+ loadComponent: () => import("./app/home/home.view").then((m) => m.HomeView),
13
+ children: [
14
+ { path: "", redirectTo: "main", pathMatch: "full" },
15
+ {
16
+ path: "main",
17
+ loadComponent: () => import("./app/home/main/main.view").then((m) => m.MainView),
18
+ },
19
+ {
20
+ path: "my-info",
21
+ loadComponent: () =>
22
+ import("./app/home/my-info/my-info.detail").then((m) => m.MyInfoDetail),
23
+ },
24
+ ],
25
+ },
26
+ ];
27
+ {{else}}
28
+ export const routes: Routes = [];
29
+ {{/if}}
@@ -11,6 +11,7 @@
11
11
  "@{{workspaceName}}/common": "workspace:*",
12
12
  "@simplysm/service-client": "^14.0.0",
13
13
  "@simplysm/service-common": "^14.0.0"{{/if}}{{#if hasDb}},
14
+ "@{{workspaceName}}/server": "workspace:*",
14
15
  "@simplysm/orm-common": "^14.0.0"{{/if}}
15
16
  },
16
17
  "devDependencies": {
@@ -1,8 +1,12 @@
1
1
  {{#if hasServer}}
2
- export { AppServiceProvider, APP_MAIN_SERVICE_KEY } from "./providers/AppServiceProvider";
2
+ export * from "./providers/app-service.provider";
3
3
  {{/if}}
4
4
  {{#if hasDb}}
5
- export { AppOrmProvider } from "./providers/AppOrmProvider";
5
+ export * from "./providers/app-orm.provider";
6
+ export * from "./providers/app-shared-data.provider";
7
+ {{/if}}
8
+ {{#if hasAuth}}
9
+ export * from "./providers/app-auth.provider";
6
10
  {{/if}}
7
11
  {{#unless hasServer}}
8
12
  {{#unless hasDb}}
@@ -0,0 +1,90 @@
1
+ import { inject, Injectable, signal } from "@angular/core";
2
+ import { SdAppStructureProvider, SdLocalStorageProvider } from "@simplysm/angular";
3
+ import { createLogger } from "@simplysm/core-common";
4
+ import type { IAuthData } from "@{{workspaceName}}/server";
5
+ import { AppServiceProvider } from "./app-service.provider";
6
+ import { AppSharedDataProvider } from "./app-shared-data.provider";
7
+
8
+ const logger = createLogger("client-common:auth");
9
+
10
+ interface IAppLocalStorageMap {
11
+ "auth-token": string;
12
+ "last-login-id": string;
13
+ }
14
+
15
+ @Injectable({ providedIn: "root" })
16
+ export class AppAuthProvider {
17
+ private readonly _appService = inject(AppServiceProvider);
18
+ private readonly _sdLocalStorage = inject(
19
+ SdLocalStorageProvider,
20
+ ) as SdLocalStorageProvider<IAppLocalStorageMap>;
21
+ private readonly _sdAppStructure = inject(SdAppStructureProvider);
22
+ private readonly _appSharedData = inject(AppSharedDataProvider);
23
+
24
+ authInfo = signal<IAuthData | undefined>(undefined);
25
+
26
+ private _authEventKey?: string;
27
+
28
+ async login(loginId: string, password: string): Promise<void> {
29
+ const { token, ...authData } = await this._appService.auth.login(loginId, password);
30
+ this._sdLocalStorage.set("last-login-id", loginId);
31
+ await this._applyAuthAsync(token, authData);
32
+ }
33
+
34
+ async logout(): Promise<void> {
35
+ await this._clearAuthAsync();
36
+ }
37
+
38
+ async tryReloadAuth(): Promise<boolean> {
39
+ const currentToken = this._sdLocalStorage.get("auth-token");
40
+ if (currentToken == null || currentToken === "") return false;
41
+
42
+ try {
43
+ await this._appService.client.auth(currentToken);
44
+ const { token, ...authData } = await this._appService.auth.refresh();
45
+ await this._applyAuthAsync(token, authData);
46
+ return true;
47
+ } catch (err) {
48
+ logger.error("인증 재로드 실패", err);
49
+ await this._clearAuthAsync();
50
+ return false;
51
+ }
52
+ }
53
+
54
+ private async _applyAuthAsync(token: string, authData: IAuthData): Promise<void> {
55
+ this._sdLocalStorage.set("auth-token", token);
56
+ await this._appService.client.auth(token);
57
+ this.authInfo.set(authData);
58
+ this._sdAppStructure.permRecord.set(authData.permissions);
59
+ await this._registerAuthEvent(authData.{{userEntityCamel}}Id);
60
+ this._appSharedData.initialize();
61
+ }
62
+
63
+ private async _clearAuthAsync(): Promise<void> {
64
+ await this._unregisterAuthEvent();
65
+ this._sdLocalStorage.remove("auth-token");
66
+ this.authInfo.set(undefined);
67
+ this._sdAppStructure.permRecord.set(undefined);
68
+ }
69
+
70
+ private async _registerAuthEvent({{userEntityCamel}}Id: number): Promise<void> {
71
+ await this._unregisterAuthEvent();
72
+ this._authEventKey = await this._appService.authInfoChangedEvent.addListener(
73
+ { {{userEntityCamel}}Id },
74
+ async () => {
75
+ await this.tryReloadAuth();
76
+ },
77
+ );
78
+ }
79
+
80
+ private async _unregisterAuthEvent(): Promise<void> {
81
+ if (this._authEventKey != null) {
82
+ await this._appService.client.removeListener(this._authEventKey);
83
+ this._authEventKey = undefined;
84
+ }
85
+ }
86
+
87
+ getLastLoginId(): string | undefined {
88
+ return this._sdLocalStorage.get("last-login-id");
89
+ }
90
+ }
@@ -1,34 +1,28 @@
1
1
  import { inject, Injectable } from "@angular/core";
2
- import { createOrmClientConnector, type OrmClientConnector } from "@simplysm/service-client";
3
2
  import { {{dbContextClassName}} } from "@{{workspaceName}}/common";
4
- import { AppServiceProvider } from "./AppServiceProvider";
3
+ import { AppServiceProvider } from "./app-service.provider";
5
4
 
6
5
  @Injectable({ providedIn: "root" })
7
6
  export class AppOrmProvider {
8
7
  private readonly _appService = inject(AppServiceProvider);
9
8
 
10
- private _orm?: OrmClientConnector;
11
- private get orm(): OrmClientConnector {
12
- return (this._orm ??= createOrmClientConnector(this._appService.client));
13
- }
14
-
15
9
  connectAsync<R>(callback: (db: {{dbContextClassName}}) => Promise<R>): Promise<R> {
16
- return this.orm.connect(
10
+ return this._appService.orm.connect(
17
11
  {
18
12
  DbClass: {{dbContextClassName}},
19
13
  connOpt: { configName: "{{dbContextNameUpper}}" },
20
- dbContextOpt: { database: "{{workspaceNameUpper}}", schema: "dbo" },
14
+ dbContextOpt: { database: "{{workspaceNameUpper}}" },
21
15
  },
22
16
  callback,
23
17
  );
24
18
  }
25
19
 
26
20
  connectWithoutTransAsync<R>(callback: (db: {{dbContextClassName}}) => Promise<R>): Promise<R> {
27
- return this.orm.connectWithoutTransaction(
21
+ return this._appService.orm.connectWithoutTransaction(
28
22
  {
29
23
  DbClass: {{dbContextClassName}},
30
24
  connOpt: { configName: "{{dbContextNameUpper}}" },
31
- dbContextOpt: { database: "{{workspaceNameUpper}}", schema: "dbo" },
25
+ dbContextOpt: { database: "{{workspaceNameUpper}}" },
32
26
  },
33
27
  callback,
34
28
  );
@@ -0,0 +1,68 @@
1
+ import { inject, Injectable } from "@angular/core";
2
+ import { SdServiceClientFactoryProvider } from "@simplysm/angular";
3
+ import { env, num, parseBoolEnv } from "@simplysm/core-common";
4
+ {{#if hasDb}}
5
+ import {
6
+ createOrmClientConnector,
7
+ type OrmClientConnector,
8
+ type ServiceProxy,
9
+ {{#if hasAuth}}
10
+ type ClientEventProxy,
11
+ {{/if}}
12
+ } from "@simplysm/service-client";
13
+ {{/if}}
14
+ {{#if hasDb}}
15
+ import type { DevServiceMethods } from "@{{workspaceName}}/server";
16
+ {{/if}}
17
+ {{#if hasAuth}}
18
+ import type { AuthServiceMethods } from "@{{workspaceName}}/server";
19
+ import { AuthInfoChangedEvent } from "@{{workspaceName}}/common";
20
+ {{/if}}
21
+
22
+ export const APP_MAIN_SERVICE_KEY = "MAIN";
23
+
24
+ @Injectable({ providedIn: "root" })
25
+ export class AppServiceProvider {
26
+ private readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
27
+
28
+ get client() {
29
+ return this._sdServiceClientFactory.get(APP_MAIN_SERVICE_KEY);
30
+ }
31
+ {{#if hasDb}}
32
+
33
+ private _orm?: OrmClientConnector;
34
+ get orm(): OrmClientConnector {
35
+ return (this._orm ??= createOrmClientConnector(this.client));
36
+ }
37
+
38
+ private _dev?: ServiceProxy<DevServiceMethods>;
39
+ get dev(): ServiceProxy<DevServiceMethods> {
40
+ return (this._dev ??= this.client.getService<DevServiceMethods>("DevService"));
41
+ }
42
+ {{/if}}
43
+ {{#if hasAuth}}
44
+
45
+ private _auth?: ServiceProxy<AuthServiceMethods>;
46
+ get auth(): ServiceProxy<AuthServiceMethods> {
47
+ return (this._auth ??= this.client.getService<AuthServiceMethods>("AuthService"));
48
+ }
49
+
50
+ private _authInfoChangedEvent?: ClientEventProxy<typeof AuthInfoChangedEvent>;
51
+ get authInfoChangedEvent(): ClientEventProxy<typeof AuthInfoChangedEvent> {
52
+ return (this._authInfoChangedEvent ??= this.client.getEvent(AuthInfoChangedEvent));
53
+ }
54
+ {{/if}}
55
+
56
+ async connectAsync() {
57
+ await this._sdServiceClientFactory.connectAsync(
58
+ APP_MAIN_SERVICE_KEY,
59
+ Boolean(env("SERVER_HOST"))
60
+ ? {
61
+ host: env("SERVER_HOST"),
62
+ port: num.parseInt(env("SERVER_PORT")),
63
+ ssl: parseBoolEnv(env("SERVER_SSL")),
64
+ }
65
+ : {},
66
+ );
67
+ }
68
+ }
@@ -0,0 +1,100 @@
1
+ import { inject, Injectable } from "@angular/core";
2
+ import {
3
+ SdSharedDataProvider,
4
+ {{#if hasAuth}}
5
+ type SharedDataBase,
6
+ {{/if}}
7
+ type SharedDataHandle,
8
+ } from "@simplysm/angular";
9
+ {{#if hasAuth}}
10
+ import { expr } from "@simplysm/orm-common";
11
+ import { AppOrmProvider } from "./app-orm.provider";
12
+ {{/if}}
13
+
14
+ export function useSharedSignal<K extends keyof TAppSharedData>(
15
+ name: K,
16
+ ): SharedDataHandle<TAppSharedData[K]> {
17
+ const appSharedData = inject(AppSharedDataProvider);
18
+ return appSharedData.getHandle(name);
19
+ }
20
+
21
+ @Injectable({ providedIn: "root" })
22
+ export class AppSharedDataProvider extends SdSharedDataProvider<TAppSharedData> {
23
+ {{#if hasAuth}}
24
+ private readonly _appOrm = inject(AppOrmProvider);
25
+
26
+ {{/if}}
27
+ override initialize() {
28
+ {{#if hasAuth}}
29
+ this.register("{{userEntityLabel}}", {
30
+ serviceKey: "{{dbContextNameUpper}}",
31
+ getter: async (changeKeys) => {
32
+ return this._appOrm.connectAsync(async (db) => {
33
+ let qr = db.{{userEntityCamel}}().select((item) => ({
34
+ id: item.id,
35
+ name: item.name,
36
+ isDeleted: item.isDeleted,
37
+
38
+ __valueKey: item.id,
39
+ __searchText: item.name,
40
+ __isHidden: item.isDeleted,
41
+ }));
42
+
43
+ if (changeKeys) {
44
+ qr = qr.where((item) => [expr.in(item.id, changeKeys as number[])]);
45
+ }
46
+ return qr.execute();
47
+ });
48
+ },
49
+ orderBy: (item) => item.name,
50
+ });
51
+
52
+ this.register("역할", {
53
+ serviceKey: "{{dbContextNameUpper}}",
54
+ getter: async (changeKeys) => {
55
+ return this._appOrm.connectAsync(async (db) => {
56
+ let qr = db.role().select((item) => ({
57
+ id: item.id,
58
+ name: item.name,
59
+ isDeleted: item.isDeleted,
60
+
61
+ __valueKey: item.id,
62
+ __searchText: item.name,
63
+ __isHidden: item.isDeleted,
64
+ }));
65
+
66
+ if (changeKeys) {
67
+ qr = qr.where((item) => [expr.in(item.id, changeKeys as number[])]);
68
+ }
69
+ return qr.execute();
70
+ });
71
+ },
72
+ orderBy: (item) => item.name,
73
+ });
74
+ {{else}}
75
+ // TODO: 공유 데이터 등록
76
+ // this.register("키", { serviceKey: "{{dbContextNameUpper}}", getter: async () => [...], orderBy: (item) => item.name });
77
+ {{/if}}
78
+ }
79
+ }
80
+
81
+ {{#if hasAuth}}
82
+ export type TAppSharedData = {
83
+ {{userEntityLabel}}: IShared{{userEntityPascal}};
84
+ 역할: ISharedRole;
85
+ };
86
+
87
+ export interface IShared{{userEntityPascal}} extends SharedDataBase<number> {
88
+ id: number;
89
+ name: string;
90
+ isDeleted: boolean;
91
+ }
92
+
93
+ export interface ISharedRole extends SharedDataBase<number> {
94
+ id: number;
95
+ name: string;
96
+ isDeleted: boolean;
97
+ }
98
+ {{else}}
99
+ export type TAppSharedData = {};
100
+ {{/if}}
@@ -5,6 +5,7 @@
5
5
  "private": true,
6
6
  "dependencies": {
7
7
  "@simplysm/core-common": "^14.0.0"{{#if hasDb}},
8
- "@simplysm/orm-common": "^14.0.0"{{/if}}
8
+ "@simplysm/orm-common": "^14.0.0"{{/if}}{{#if hasAuth}},
9
+ "@simplysm/service-common": "^14.0.0"{{/if}}
9
10
  }
10
11
  }
@@ -0,0 +1,26 @@
1
+ import type { AppStructureItem } from "@simplysm/service-common";
2
+
3
+ {{#each appStructureNames}}
4
+ export const {{this}}: AppStructureItem[] = [
5
+ { title: "메인화면", code: "main", isNotMenu: true },
6
+ { title: "내 정보 수정", code: "my-info", isNotMenu: true },
7
+
8
+ {
9
+ code: "base",
10
+ title: "기준정보",
11
+ children: [
12
+ { code: "role-permission", title: "역할/권한", perms: ["use", "edit"] },
13
+ { code: "{{../userEntityKebab}}", title: "{{../userEntityLabel}}", perms: ["use", "edit"] },
14
+ ],
15
+ },
16
+ {
17
+ code: "system",
18
+ title: "시스템",
19
+ children: [
20
+ { code: "data-log", title: "데이터 변경내역", perms: ["use"] },
21
+ { code: "system-log", title: "시스템 로그", perms: ["use"] },
22
+ ],
23
+ },
24
+ ];
25
+
26
+ {{/each}}
@@ -0,0 +1,3 @@
1
+ import { defineEvent } from "@simplysm/service-common";
2
+
3
+ export const AuthInfoChangedEvent = defineEvent<{ {{userEntityCamel}}Id: number }, void>("AuthInfoChanged");
@@ -0,0 +1,20 @@
1
+ import { DbContext } from "@simplysm/orm-common";
2
+ {{#if hasAuth}}
3
+ import { {{userEntityPascal}} } from "./tables/master/{{userEntityKebab}}";
4
+ import { {{userEntityPascal}}Config } from "./tables/master/{{userEntityKebab}}-config";
5
+ import { Role } from "./tables/system/role";
6
+ import { RolePermission } from "./tables/system/role-permission";
7
+ {{/if}}
8
+ import { SystemDataLog } from "./tables/system/system-data-log";
9
+ import { SystemLog } from "./tables/system/system-log";
10
+
11
+ export class {{dbContextClassName}} extends DbContext {
12
+ {{#if hasAuth}}
13
+ {{userEntityCamel}} = this.queryable({{userEntityPascal}});
14
+ {{userEntityCamel}}Config = this.queryable({{userEntityPascal}}Config);
15
+ role = this.queryable(Role);
16
+ rolePermission = this.queryable(RolePermission);
17
+ {{/if}}
18
+ dataLog = this.queryable(SystemDataLog);
19
+ systemLog = this.queryable(SystemLog);
20
+ }