user-authentication
This commit is contained in:
		
							
								
								
									
										3
									
								
								examples/user-authentication/backend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/user-authentication/backend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| node_modules | ||||
| dist | ||||
| .DS_STORE | ||||
							
								
								
									
										30
									
								
								examples/user-authentication/backend/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/user-authentication/backend/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| { | ||||
|     "configurations": [ | ||||
|         { | ||||
|             "type": "node", | ||||
|             "request": "launch", | ||||
|             "name": "mocha current file", | ||||
|             "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", | ||||
|             "args": [ | ||||
|                 "${file}" | ||||
|             ], | ||||
|             "internalConsoleOptions": "openOnSessionStart", | ||||
|             "cwd": "${workspaceFolder}" | ||||
|         }, | ||||
|         { | ||||
|             "type": "node", | ||||
|             "request": "launch", | ||||
|             "name": "ts-node current file", | ||||
|             "protocol": "inspector", | ||||
|             "args": [ | ||||
|                 "${relativeFile}" | ||||
|             ], | ||||
|             "cwd": "${workspaceRoot}", | ||||
|             "runtimeArgs": [ | ||||
|                 "-r", | ||||
|                 "ts-node/register" | ||||
|             ], | ||||
|             "internalConsoleOptions": "openOnSessionStart" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										3
									
								
								examples/user-authentication/backend/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/user-authentication/backend/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| { | ||||
|     "typescript.tsdk": "node_modules\\typescript\\lib" | ||||
| } | ||||
							
								
								
									
										35
									
								
								examples/user-authentication/backend/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								examples/user-authentication/backend/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| # TSRPC Server | ||||
|  | ||||
| ## Run | ||||
| ### Local Dev Server | ||||
| ``` | ||||
| npm run dev | ||||
| ``` | ||||
|  | ||||
|  | ||||
|  | ||||
| ### Build | ||||
| ``` | ||||
| npm run build | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Files | ||||
| ### Generate ServiceProto | ||||
| ``` | ||||
| npm run proto | ||||
| ``` | ||||
|  | ||||
| ### Generate API templates | ||||
| ``` | ||||
| npm run api | ||||
| ``` | ||||
|  | ||||
| ### Sync shared code to client | ||||
|  | ||||
| ``` | ||||
| npm run sync | ||||
| ``` | ||||
|  | ||||
| > If you chose symlink when using `create-tsrpc-app`, it would re-create the symlink instead of copy files. | ||||
							
								
								
									
										25
									
								
								examples/user-authentication/backend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								examples/user-authentication/backend/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| { | ||||
|   "name": "user-authentication-backend", | ||||
|   "version": "0.1.0", | ||||
|   "main": "index.js", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "proto": "tsrpc proto -i src/shared/protocols -o src/shared/protocols/serviceProto.ts", | ||||
|     "sync": "tsrpc sync --from src/shared --to ../frontend/src/shared", | ||||
|     "api": "tsrpc api -i src/shared/protocols/serviceProto.ts -o src/api", | ||||
|     "dev": "onchange \"src/**/*.ts\" -i -k -- ts-node \"src/index.ts\"", | ||||
|     "build": "tsrpc build" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/node": "^15.12.2", | ||||
|     "@types/uuid": "^8.3.0", | ||||
|     "onchange": "^7.1.0", | ||||
|     "ts-node": "^9.1.1", | ||||
|     "tsrpc-cli": "^2.0.1-dev.12", | ||||
|     "typescript": "^4.3.2" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "tsrpc": "^3.0.0-dev.20", | ||||
|     "uuid": "^8.3.2" | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| import { ApiCall } from "tsrpc"; | ||||
| import { ReqAdminAction, ResAdminAction } from "../../shared/protocols/action/PtlAdminAction"; | ||||
|  | ||||
| export async function ApiAdminAction(call: ApiCall<ReqAdminAction, ResAdminAction>) { | ||||
|     call.succ({ | ||||
|         result: 'Success' | ||||
|     }) | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| import { ApiCall } from "tsrpc"; | ||||
| import { ReqGuestAction, ResGuestAction } from "../../shared/protocols/action/PtlGuestAction"; | ||||
|  | ||||
| export async function ApiGuestAction(call: ApiCall<ReqGuestAction, ResGuestAction>) { | ||||
|     call.succ({ | ||||
|         result: 'Success' | ||||
|     }) | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| import { ApiCall } from "tsrpc"; | ||||
| import { ReqNormalAction, ResNormalAction } from "../../shared/protocols/action/PtlNormalAction"; | ||||
|  | ||||
| export async function ApiNormalAction(call: ApiCall<ReqNormalAction, ResNormalAction>) { | ||||
|     call.succ({ | ||||
|         result: 'Success' | ||||
|     }) | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| import { ApiCall } from "tsrpc"; | ||||
| import { UserUtil } from "../../models/UserUtil"; | ||||
| import { ReqLogin, ResLogin } from "../../shared/protocols/user/PtlLogin"; | ||||
|  | ||||
| export async function ApiLogin(call: ApiCall<ReqLogin, ResLogin>) { | ||||
|     let user = UserUtil.users.find(v => v.username === call.req.username && v.password === call.req.password); | ||||
|     if (!user) { | ||||
|         call.error('Error username or password'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     let sso = await UserUtil.createSsoToken(user.uid); | ||||
|  | ||||
|     call.succ({ | ||||
|         __ssoToken: sso | ||||
|     }) | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
| import { ApiCall } from "tsrpc"; | ||||
| import { UserUtil } from "../../models/UserUtil"; | ||||
| import { ReqLogout, ResLogout } from "../../shared/protocols/user/PtlLogout"; | ||||
|  | ||||
| export async function ApiLogout(call: ApiCall<ReqLogout, ResLogout>) { | ||||
|     call.req.__ssoToken && UserUtil.destroySsoToken(call.req.__ssoToken); | ||||
|     call.succ({ | ||||
|         __ssoToken: '' | ||||
|     }); | ||||
| } | ||||
							
								
								
									
										31
									
								
								examples/user-authentication/backend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								examples/user-authentication/backend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import * as path from "path"; | ||||
| import { HttpServer } from "tsrpc"; | ||||
| import { enableAuthentication } from "./models/enableAuthentication"; | ||||
| import { parseCurrentUser } from "./models/parseCurrentUser"; | ||||
| import { serviceProto } from "./shared/protocols/serviceProto"; | ||||
|  | ||||
| // Create the Server | ||||
| const server = new HttpServer(serviceProto, { | ||||
|     port: 3000, | ||||
|     cors: '*' | ||||
| }); | ||||
|  | ||||
| parseCurrentUser(server); | ||||
| enableAuthentication(server); | ||||
|  | ||||
| // Entry function | ||||
| async function main() { | ||||
|     // Auto implement APIs | ||||
|     await server.autoImplementApi(path.resolve(__dirname, 'api')); | ||||
|  | ||||
|     // TODO | ||||
|     // Prepare something... (e.g. connect the db) | ||||
|  | ||||
|     await server.start(); | ||||
| }; | ||||
|  | ||||
| main().catch(e => { | ||||
|     // Exit if any error during the startup | ||||
|     server.logger.error(e); | ||||
|     process.exit(-1); | ||||
| }); | ||||
| @@ -0,0 +1,5 @@ | ||||
| export interface CurrentUser { | ||||
|     uid: number, | ||||
|     username: string, | ||||
|     roles: string[] | ||||
| } | ||||
							
								
								
									
										76
									
								
								examples/user-authentication/backend/src/models/UserUtil.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								examples/user-authentication/backend/src/models/UserUtil.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| import * as uuid from "uuid"; | ||||
| import { CurrentUser } from "./CurrentUser"; | ||||
|  | ||||
| const SSO_VALID_TIME = 86400000 * 7; | ||||
|  | ||||
| export class UserUtil { | ||||
|  | ||||
|     // Store data in memory for test | ||||
|     // You can store data into database | ||||
|     static users: { | ||||
|         uid: number, | ||||
|         username: string, | ||||
|         password: string, | ||||
|         roles: string[] | ||||
|     }[] = [ | ||||
|             { | ||||
|                 uid: 1, | ||||
|                 username: 'Normal', | ||||
|                 password: '123456', | ||||
|                 roles: ['Normal'] | ||||
|             }, | ||||
|             { | ||||
|                 uid: 2, | ||||
|                 username: 'Admin', | ||||
|                 password: '123456', | ||||
|                 roles: ['Admin'] | ||||
|             } | ||||
|         ]; | ||||
|  | ||||
|     static ssoTokenInfo: { | ||||
|         [token: string]: { expiredTime: number, uid: number } | ||||
|     } = {}; | ||||
|  | ||||
|     static async createSsoToken(uid: number): Promise<string> { | ||||
|         let token = uuid.v1(); | ||||
|         // Expired after some time without any action | ||||
|         let expiredTime = Date.now() + SSO_VALID_TIME; | ||||
|  | ||||
|         this.ssoTokenInfo[token] = { | ||||
|             uid: uid, | ||||
|             expiredTime: expiredTime | ||||
|         }; | ||||
|  | ||||
|         return token; | ||||
|     } | ||||
|  | ||||
|     static async destroySsoToken(ssoToken: string): Promise<void> { | ||||
|         delete this.ssoTokenInfo[ssoToken]; | ||||
|     } | ||||
|  | ||||
|     static async parseSSO(ssoToken: string): Promise<CurrentUser | undefined> { | ||||
|         let info = this.ssoTokenInfo[ssoToken]; | ||||
|         // Token not exists or expired | ||||
|         if (!info || info.expiredTime < Date.now()) { | ||||
|             return undefined; | ||||
|         } | ||||
|  | ||||
|         // Parse User | ||||
|         let user = this.users.find(v => v.uid === info.uid); | ||||
|         if (!user) { | ||||
|             return undefined; | ||||
|         } | ||||
|  | ||||
|         // Extend expired time | ||||
|         info.expiredTime = Date.now() + SSO_VALID_TIME; | ||||
|  | ||||
|         // Return parsed CurrentUser | ||||
|         return { | ||||
|             uid: user.uid, | ||||
|             username: user.username, | ||||
|             roles: user.roles | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| import { HttpServer } from "tsrpc"; | ||||
| import { BaseConf } from "../shared/protocols/base"; | ||||
|  | ||||
| export function enableAuthentication(server: HttpServer) { | ||||
|     server.flows.preApiCallFlow.push(call => { | ||||
|         let conf: BaseConf | undefined = call.service.conf; | ||||
|  | ||||
|         // NeedLogin | ||||
|         if (conf?.needLogin && !call.currentUser) { | ||||
|             call.error('You need login before do this', { code: 'NEED_LOGIN' }); | ||||
|             return undefined; | ||||
|         } | ||||
|  | ||||
|         // NeedRoles | ||||
|         if (conf?.needRoles?.length && !call.currentUser?.roles.some(v => conf!.needRoles!.indexOf(v) > -1)) { | ||||
|             call.error('You do NOT have authority to do this', { code: 'NO_AUTHORITY' }); | ||||
|             return undefined; | ||||
|         } | ||||
|  | ||||
|         return call; | ||||
|     }) | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| import { HttpServer } from "tsrpc"; | ||||
| import { BaseRequest } from "../shared/protocols/base"; | ||||
| import { CurrentUser } from "./CurrentUser"; | ||||
| import { UserUtil } from "./UserUtil"; | ||||
|  | ||||
| export function parseCurrentUser(server: HttpServer) { | ||||
|     // Auto parse call.currentUser | ||||
|     server.flows.preApiCallFlow.push(async call => { | ||||
|         let req = call.req as BaseRequest; | ||||
|         if (req.__ssoToken) { | ||||
|             call.currentUser = await UserUtil.parseSSO(req.__ssoToken); | ||||
|         } | ||||
|         return call; | ||||
|     }) | ||||
| } | ||||
|  | ||||
| declare module 'tsrpc' { | ||||
|     export interface ApiCall { | ||||
|         currentUser?: CurrentUser; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| import { BaseRequest, BaseResponse, BaseConf } from '../base' | ||||
|  | ||||
| export interface ReqAdminAction extends BaseRequest { | ||||
|      | ||||
| } | ||||
|  | ||||
| export interface ResAdminAction extends BaseResponse { | ||||
|     result: string | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|     needLogin: true, | ||||
|     needRoles: ['Admin'] | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| import { BaseConf, BaseRequest, BaseResponse } from '../base'; | ||||
|  | ||||
| export interface ReqGuestAction extends BaseRequest { | ||||
|  | ||||
| } | ||||
|  | ||||
| export interface ResGuestAction extends BaseResponse { | ||||
|     result: string | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|     needLogin: false | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| import { BaseConf, BaseRequest, BaseResponse } from '../base'; | ||||
|  | ||||
| export interface ReqNormalAction extends BaseRequest { | ||||
|  | ||||
| } | ||||
|  | ||||
| export interface ResNormalAction extends BaseResponse { | ||||
|     result: string | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|     needLogin: true | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| export interface BaseRequest { | ||||
|     __ssoToken?: string; | ||||
| } | ||||
|  | ||||
| export interface BaseResponse { | ||||
|     // Init or refresh sso token | ||||
|     __ssoToken?: string; | ||||
| } | ||||
|  | ||||
| export interface BaseConf { | ||||
|     needLogin?: boolean, | ||||
|     needRoles?: string[] | ||||
| } | ||||
| @@ -0,0 +1,279 @@ | ||||
| import { ServiceProto } from 'tsrpc-proto'; | ||||
| import { ReqAdminAction, ResAdminAction } from './action/PtlAdminAction'; | ||||
| import { ReqGuestAction, ResGuestAction } from './action/PtlGuestAction'; | ||||
| import { ReqNormalAction, ResNormalAction } from './action/PtlNormalAction'; | ||||
| import { ReqLogin, ResLogin } from './user/PtlLogin'; | ||||
| import { ReqLogout, ResLogout } from './user/PtlLogout'; | ||||
|  | ||||
| export interface ServiceType { | ||||
|     api: { | ||||
|         "action/AdminAction": { | ||||
|             req: ReqAdminAction, | ||||
|             res: ResAdminAction | ||||
|         }, | ||||
|         "action/GuestAction": { | ||||
|             req: ReqGuestAction, | ||||
|             res: ResGuestAction | ||||
|         }, | ||||
|         "action/NormalAction": { | ||||
|             req: ReqNormalAction, | ||||
|             res: ResNormalAction | ||||
|         }, | ||||
|         "user/Login": { | ||||
|             req: ReqLogin, | ||||
|             res: ResLogin | ||||
|         }, | ||||
|         "user/Logout": { | ||||
|             req: ReqLogout, | ||||
|             res: ResLogout | ||||
|         } | ||||
|     }, | ||||
|     msg: { | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| export const serviceProto: ServiceProto<ServiceType> = { | ||||
|     "version": 2, | ||||
|     "services": [ | ||||
|         { | ||||
|             "id": 0, | ||||
|             "name": "action/AdminAction", | ||||
|             "type": "api", | ||||
|             "conf": { | ||||
|                 "needLogin": true, | ||||
|                 "needRoles": [ | ||||
|                     "Admin" | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "id": 1, | ||||
|             "name": "action/GuestAction", | ||||
|             "type": "api", | ||||
|             "conf": { | ||||
|                 "needLogin": false | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "id": 2, | ||||
|             "name": "action/NormalAction", | ||||
|             "type": "api", | ||||
|             "conf": { | ||||
|                 "needLogin": true | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "id": 3, | ||||
|             "name": "user/Login", | ||||
|             "type": "api", | ||||
|             "conf": {} | ||||
|         }, | ||||
|         { | ||||
|             "id": 4, | ||||
|             "name": "user/Logout", | ||||
|             "type": "api", | ||||
|             "conf": {} | ||||
|         } | ||||
|     ], | ||||
|     "types": { | ||||
|         "action/PtlAdminAction/ReqAdminAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "base/BaseRequest": { | ||||
|             "type": "Interface", | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "__ssoToken", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     }, | ||||
|                     "optional": true | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlAdminAction/ResAdminAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "result", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "base/BaseResponse": { | ||||
|             "type": "Interface", | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "__ssoToken", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     }, | ||||
|                     "optional": true | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlGuestAction/ReqGuestAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlGuestAction/ResGuestAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "result", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlNormalAction/ReqNormalAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlNormalAction/ResNormalAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "result", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogin/ReqLogin": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "username", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     "id": 1, | ||||
|                     "name": "password", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogin/ResLogin": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "__ssoToken", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogout/ReqLogout": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogout/ResLogout": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| @@ -0,0 +1,14 @@ | ||||
| import { BaseConf, BaseRequest, BaseResponse } from '../base'; | ||||
|  | ||||
| export interface ReqLogin extends BaseRequest { | ||||
|     username: string, | ||||
|     password: string | ||||
| } | ||||
|  | ||||
| export interface ResLogin extends BaseResponse { | ||||
|     __ssoToken: string; | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|  | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| import { BaseRequest, BaseResponse, BaseConf } from '../base' | ||||
|  | ||||
| export interface ReqLogout extends BaseRequest { | ||||
|      | ||||
| } | ||||
|  | ||||
| export interface ResLogout extends BaseResponse { | ||||
|      | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|      | ||||
| }; | ||||
							
								
								
									
										18
									
								
								examples/user-authentication/backend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								examples/user-authentication/backend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "lib": [ | ||||
|       "es2018" | ||||
|     ], | ||||
|     "module": "commonjs", | ||||
|     "target": "es2018", | ||||
|     "outDir": "dist", | ||||
|     "strict": true, | ||||
|     "esModuleInterop": true, | ||||
|     "skipLibCheck": true, | ||||
|     "forceConsistentCasingInFileNames": true, | ||||
|     "moduleResolution": "node" | ||||
|   }, | ||||
|   "include": [ | ||||
|     "src" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										3
									
								
								examples/user-authentication/frontend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/user-authentication/frontend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| .DS_STORE | ||||
| node_modules | ||||
| dist | ||||
							
								
								
									
										21
									
								
								examples/user-authentication/frontend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/user-authentication/frontend/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| { | ||||
|   "name": "user-authentication-frontend", | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "dev": "webpack serve --mode=development --open", | ||||
|     "build": "webpack --mode=production" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "copy-webpack-plugin": "^9.0.0", | ||||
|     "html-webpack-plugin": "^5.3.1", | ||||
|     "ts-loader": "^9.2.3", | ||||
|     "typescript": "^4.3.2", | ||||
|     "webpack": "^5.38.1", | ||||
|     "webpack-cli": "^4.7.2", | ||||
|     "webpack-dev-server": "^3.11.2" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "tsrpc-browser": "^3.0.0-dev.16" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										99
									
								
								examples/user-authentication/frontend/public/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								examples/user-authentication/frontend/public/index.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| * { | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     box-sizing: border-box; | ||||
| } | ||||
|  | ||||
| body { | ||||
|     padding: 20px; | ||||
| } | ||||
|  | ||||
| h1, | ||||
| h2 { | ||||
|     text-align: center; | ||||
|     margin-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .status { | ||||
|     width: 1000px; | ||||
|     margin: 20px auto; | ||||
|     text-align: center; | ||||
|     background: #f2f2f2; | ||||
|     padding: 20px; | ||||
| } | ||||
|  | ||||
| .status.logined { | ||||
|     background: #2c81b9; | ||||
|     color: #fff; | ||||
| } | ||||
|  | ||||
| .row { | ||||
|     display: flex; | ||||
|     background-color: #f2f2f2; | ||||
|     padding: 20px; | ||||
|     margin: 20px auto 0 auto; | ||||
|     width: 1000px; | ||||
| } | ||||
|  | ||||
| .row>* { | ||||
|     flex: 1; | ||||
|     margin-right: 20px; | ||||
|     padding: 20px; | ||||
|     background: #d9d9d9; | ||||
| } | ||||
|  | ||||
| .row>*:last-child { | ||||
|     margin-right: 0; | ||||
| } | ||||
|  | ||||
| label { | ||||
|     display: inline-block; | ||||
|     width: 40%; | ||||
|     line-height: 1.5rem; | ||||
| } | ||||
|  | ||||
| input { | ||||
|     display: inline-block; | ||||
|     width: 60%; | ||||
|     padding: 10px; | ||||
|     font-size: 1rem; | ||||
| } | ||||
|  | ||||
| p { | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| button { | ||||
|     font-size: 20px; | ||||
|     padding: 10px 20px; | ||||
|     cursor: pointer; | ||||
|     display: block; | ||||
|     margin: 10px auto; | ||||
| } | ||||
|  | ||||
| .action>div { | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .action .hint { | ||||
|     font-size: 14px; | ||||
|     color: #999; | ||||
| } | ||||
|  | ||||
| .action h3 { | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| pre { | ||||
|     background: #333; | ||||
|     border-radius: 5px; | ||||
|     padding: 10px; | ||||
|     color: #fff; | ||||
|     min-height: 80px; | ||||
|     text-align: left; | ||||
|     white-space: pre-wrap; | ||||
| } | ||||
|  | ||||
| .return { | ||||
|     display: none; | ||||
| } | ||||
							
								
								
									
										68
									
								
								examples/user-authentication/frontend/public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								examples/user-authentication/frontend/public/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|  | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>TSRPC Example</title> | ||||
|     <link rel="stylesheet" href="index.css" /> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <h1>Login and Role Authentication</h1> | ||||
|  | ||||
|     <div class="status">Not Logined</div> | ||||
|  | ||||
|     <div class="row user"> | ||||
|         <div class="login-normal"> | ||||
|             <h2>Normal</h2> | ||||
|             <p><button>Login as Normal Role</button></p> | ||||
|         </div> | ||||
|  | ||||
|         <div class="login-admin"> | ||||
|             <h2>Admin</h2> | ||||
|             <p><button>Login as Admin Role</button></p> | ||||
|         </div> | ||||
|  | ||||
|         <div class="logout"> | ||||
|             <h2>Logout</h2> | ||||
|             <p><button>Logout</button></p> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="row action"> | ||||
|         <div class="normal"> | ||||
|             <h2>Normal Action</h2> | ||||
|             <p class="hint">Need login</p> | ||||
|             <p><button>Do This</button></p> | ||||
|             <div class="return"> | ||||
|                 <h3>Return</h3> | ||||
|                 <pre></pre> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="admin"> | ||||
|             <h2>Admin Action</h2> | ||||
|             <p class="hint">Need Admin role</p> | ||||
|             <p><button>Do This</button></p> | ||||
|             <div class="return"> | ||||
|                 <h3>Return</h3> | ||||
|                 <pre></pre> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="guest"> | ||||
|             <h2>Guest Action</h2> | ||||
|             <p class="hint">Do NOT need login</p> | ||||
|             <p><button>Do This</button></p> | ||||
|             <div class="return"> | ||||
|                 <h3>Return</h3> | ||||
|                 <pre></pre> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										108
									
								
								examples/user-authentication/frontend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								examples/user-authentication/frontend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| import { HttpClient } from 'tsrpc-browser'; | ||||
| import { BaseResponse } from './shared/protocols/base'; | ||||
| import { serviceProto } from './shared/protocols/serviceProto'; | ||||
|  | ||||
| const $ = document.querySelector.bind(document) as (v: string) => HTMLElement; | ||||
|  | ||||
| // Create Client | ||||
| let client = new HttpClient(serviceProto, { | ||||
|     server: 'http://127.0.0.1:3000', | ||||
|     logger: console | ||||
| }); | ||||
|  | ||||
| // Flow | ||||
| client.flows.postApiReturnFlow.push(v => { | ||||
|     if (v.return.isSucc) { | ||||
|         let res = v.return.res as BaseResponse; | ||||
|         if (res.__ssoToken !== undefined) { | ||||
|             localStorage.setItem('SSO_TOKEN', res.__ssoToken); | ||||
|         } | ||||
|     } | ||||
|     else if (v.return.err.code === 'NEED_LOGIN') { | ||||
|         localStorage.removeItem('SSO_TOKEN'); | ||||
|         setStatus(false); | ||||
|     } | ||||
|     return v; | ||||
| }); | ||||
| client.flows.preCallApiFlow.push(v => { | ||||
|     let ssoToken = localStorage.getItem('SSO_TOKEN'); | ||||
|     if (ssoToken) { | ||||
|         v.req.__ssoToken = ssoToken; | ||||
|     } | ||||
|     return v; | ||||
| }) | ||||
|  | ||||
| // User | ||||
| $('.login-normal button').onclick = async () => { | ||||
|     let ret = await client.callApi('user/Login', { | ||||
|         username: 'Normal', | ||||
|         password: '123456' | ||||
|     }); | ||||
|  | ||||
|     if (!ret.isSucc) { | ||||
|         alert(ret.err.message); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     localStorage.setItem('LoginedRole', 'Normal'); | ||||
|     setStatus(true); | ||||
|     document.querySelectorAll<HTMLElement>('.return').forEach(v => { v.style.display = 'none' }); | ||||
| } | ||||
| $('.login-admin button').onclick = async () => { | ||||
|     let ret = await client.callApi('user/Login', { | ||||
|         username: 'Admin', | ||||
|         password: '123456' | ||||
|     }); | ||||
|  | ||||
|     if (!ret.isSucc) { | ||||
|         alert(ret.err.message); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     localStorage.setItem('LoginedRole', 'Admin'); | ||||
|     setStatus(true); | ||||
|     document.querySelectorAll<HTMLElement>('.return').forEach(v => { v.style.display = 'none' }); | ||||
| } | ||||
| $('.logout button').onclick = async () => { | ||||
|     let ret = await client.callApi('user/Logout', {}); | ||||
|  | ||||
|     if (!ret.isSucc) { | ||||
|         alert(ret.err.message); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     setStatus(false); | ||||
|     document.querySelectorAll<HTMLElement>('.return').forEach(v => { v.style.display = 'none' }); | ||||
| } | ||||
|  | ||||
| // Actions | ||||
| $('.action .guest button').onclick = async () => { | ||||
|     let ret = await client.callApi('action/GuestAction', {}); | ||||
|     $('.action .guest pre').innerText = JSON.stringify(ret, null, 2); | ||||
|     $('.action .guest pre').style.background = ret.isSucc ? 'green' : 'darkred'; | ||||
|     $('.action .guest .return').style.display = 'block'; | ||||
| } | ||||
| $('.action .normal button').onclick = async () => { | ||||
|     let ret = await client.callApi('action/NormalAction', {}); | ||||
|     $('.action .normal pre').innerText = JSON.stringify(ret, null, 2); | ||||
|     $('.action .normal pre').style.background = ret.isSucc ? 'green' : 'darkred'; | ||||
|     $('.action .normal .return').style.display = 'block'; | ||||
| } | ||||
| $('.action .admin button').onclick = async () => { | ||||
|     let ret = await client.callApi('action/AdminAction', {}); | ||||
|     $('.action .admin pre').innerText = JSON.stringify(ret, null, 2); | ||||
|     $('.action .admin pre').style.background = ret.isSucc ? 'green' : 'darkred'; | ||||
|     $('.action .admin .return').style.display = 'block'; | ||||
| } | ||||
|  | ||||
| function setStatus(logined: boolean) { | ||||
|     if (logined) { | ||||
|         $('.status').className = 'status logined'; | ||||
|         $('.status').innerText = `Logined as ${localStorage.getItem('LoginedRole')} Role`; | ||||
|     } | ||||
|     else { | ||||
|         $('.status').className = 'status'; | ||||
|         $('.status').innerText = 'Not Logined'; | ||||
|     } | ||||
| } | ||||
| setStatus(!!localStorage.getItem('SSO_TOKEN')); | ||||
| @@ -0,0 +1,14 @@ | ||||
| import { BaseRequest, BaseResponse, BaseConf } from '../base' | ||||
|  | ||||
| export interface ReqAdminAction extends BaseRequest { | ||||
|      | ||||
| } | ||||
|  | ||||
| export interface ResAdminAction extends BaseResponse { | ||||
|     result: string | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|     needLogin: true, | ||||
|     needRoles: ['Admin'] | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| import { BaseConf, BaseRequest, BaseResponse } from '../base'; | ||||
|  | ||||
| export interface ReqGuestAction extends BaseRequest { | ||||
|  | ||||
| } | ||||
|  | ||||
| export interface ResGuestAction extends BaseResponse { | ||||
|     result: string | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|     needLogin: false | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| import { BaseConf, BaseRequest, BaseResponse } from '../base'; | ||||
|  | ||||
| export interface ReqNormalAction extends BaseRequest { | ||||
|  | ||||
| } | ||||
|  | ||||
| export interface ResNormalAction extends BaseResponse { | ||||
|     result: string | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|     needLogin: true | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| export interface BaseRequest { | ||||
|     __ssoToken?: string; | ||||
| } | ||||
|  | ||||
| export interface BaseResponse { | ||||
|     // Init or refresh sso token | ||||
|     __ssoToken?: string; | ||||
| } | ||||
|  | ||||
| export interface BaseConf { | ||||
|     needLogin?: boolean, | ||||
|     needRoles?: string[] | ||||
| } | ||||
| @@ -0,0 +1,279 @@ | ||||
| import { ServiceProto } from 'tsrpc-proto'; | ||||
| import { ReqAdminAction, ResAdminAction } from './action/PtlAdminAction'; | ||||
| import { ReqGuestAction, ResGuestAction } from './action/PtlGuestAction'; | ||||
| import { ReqNormalAction, ResNormalAction } from './action/PtlNormalAction'; | ||||
| import { ReqLogin, ResLogin } from './user/PtlLogin'; | ||||
| import { ReqLogout, ResLogout } from './user/PtlLogout'; | ||||
|  | ||||
| export interface ServiceType { | ||||
|     api: { | ||||
|         "action/AdminAction": { | ||||
|             req: ReqAdminAction, | ||||
|             res: ResAdminAction | ||||
|         }, | ||||
|         "action/GuestAction": { | ||||
|             req: ReqGuestAction, | ||||
|             res: ResGuestAction | ||||
|         }, | ||||
|         "action/NormalAction": { | ||||
|             req: ReqNormalAction, | ||||
|             res: ResNormalAction | ||||
|         }, | ||||
|         "user/Login": { | ||||
|             req: ReqLogin, | ||||
|             res: ResLogin | ||||
|         }, | ||||
|         "user/Logout": { | ||||
|             req: ReqLogout, | ||||
|             res: ResLogout | ||||
|         } | ||||
|     }, | ||||
|     msg: { | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| export const serviceProto: ServiceProto<ServiceType> = { | ||||
|     "version": 2, | ||||
|     "services": [ | ||||
|         { | ||||
|             "id": 0, | ||||
|             "name": "action/AdminAction", | ||||
|             "type": "api", | ||||
|             "conf": { | ||||
|                 "needLogin": true, | ||||
|                 "needRoles": [ | ||||
|                     "Admin" | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "id": 1, | ||||
|             "name": "action/GuestAction", | ||||
|             "type": "api", | ||||
|             "conf": { | ||||
|                 "needLogin": false | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "id": 2, | ||||
|             "name": "action/NormalAction", | ||||
|             "type": "api", | ||||
|             "conf": { | ||||
|                 "needLogin": true | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "id": 3, | ||||
|             "name": "user/Login", | ||||
|             "type": "api", | ||||
|             "conf": {} | ||||
|         }, | ||||
|         { | ||||
|             "id": 4, | ||||
|             "name": "user/Logout", | ||||
|             "type": "api", | ||||
|             "conf": {} | ||||
|         } | ||||
|     ], | ||||
|     "types": { | ||||
|         "action/PtlAdminAction/ReqAdminAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "base/BaseRequest": { | ||||
|             "type": "Interface", | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "__ssoToken", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     }, | ||||
|                     "optional": true | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlAdminAction/ResAdminAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "result", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "base/BaseResponse": { | ||||
|             "type": "Interface", | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "__ssoToken", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     }, | ||||
|                     "optional": true | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlGuestAction/ReqGuestAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlGuestAction/ResGuestAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "result", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlNormalAction/ReqNormalAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "action/PtlNormalAction/ResNormalAction": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "result", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogin/ReqLogin": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "username", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     "id": 1, | ||||
|                     "name": "password", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogin/ResLogin": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             "properties": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "name": "__ssoToken", | ||||
|                     "type": { | ||||
|                         "type": "String" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogout/ReqLogout": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseRequest" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "user/PtlLogout/ResLogout": { | ||||
|             "type": "Interface", | ||||
|             "extends": [ | ||||
|                 { | ||||
|                     "id": 0, | ||||
|                     "type": { | ||||
|                         "type": "Reference", | ||||
|                         "target": "base/BaseResponse" | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| @@ -0,0 +1,14 @@ | ||||
| import { BaseConf, BaseRequest, BaseResponse } from '../base'; | ||||
|  | ||||
| export interface ReqLogin extends BaseRequest { | ||||
|     username: string, | ||||
|     password: string | ||||
| } | ||||
|  | ||||
| export interface ResLogin extends BaseResponse { | ||||
|     __ssoToken: string; | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|  | ||||
| }; | ||||
| @@ -0,0 +1,13 @@ | ||||
| import { BaseRequest, BaseResponse, BaseConf } from '../base' | ||||
|  | ||||
| export interface ReqLogout extends BaseRequest { | ||||
|      | ||||
| } | ||||
|  | ||||
| export interface ResLogout extends BaseResponse { | ||||
|      | ||||
| } | ||||
|  | ||||
| export const conf: BaseConf = { | ||||
|      | ||||
| }; | ||||
							
								
								
									
										23
									
								
								examples/user-authentication/frontend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								examples/user-authentication/frontend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "lib": [ | ||||
|       "dom", | ||||
|       "es2018" | ||||
|     ], | ||||
|     "module": "esnext", | ||||
|     "target": "es2018", | ||||
|     "allowSyntheticDefaultImports": true, | ||||
|     "esModuleInterop": true, | ||||
|     "forceConsistentCasingInFileNames": true, | ||||
|     "moduleResolution": "node", | ||||
|     "outDir": "dist", | ||||
|     "skipLibCheck": true, | ||||
|     "strict": true, | ||||
|     "jsx": "react-jsx", | ||||
|     "sourceMap": true, | ||||
|     "isolatedModules": true | ||||
|   }, | ||||
|   "include": [ | ||||
|     "src" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										58
									
								
								examples/user-authentication/frontend/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								examples/user-authentication/frontend/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| const webpack = require('webpack'); | ||||
| const path = require('path'); | ||||
| const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||||
| const CopyWebpackPlugin = require('copy-webpack-plugin'); | ||||
|  | ||||
| const isProduction = process.argv.indexOf('--mode=production') > -1; | ||||
|  | ||||
| module.exports = { | ||||
|     entry: './src/index.ts', | ||||
|     output: { | ||||
|         filename: 'bundle.[contenthash].js', | ||||
|         path: path.resolve(__dirname, 'dist'), | ||||
|         clean: true | ||||
|     }, | ||||
|     resolve: { | ||||
|         extensions: ['.ts', '.tsx', '.js'] | ||||
|     }, | ||||
|     module: { | ||||
|         rules: [ | ||||
|             { | ||||
|                 test: /\.tsx?$/, | ||||
|                 use: [{ | ||||
|                     loader: 'ts-loader', | ||||
|                     options: { | ||||
|                         compilerOptions: isProduction ? { | ||||
|                             "lib": [ | ||||
|                                 "dom", | ||||
|                                 "es2015.promise" | ||||
|                             ], | ||||
|                             "target": "es5", | ||||
|                         } : undefined | ||||
|                     } | ||||
|                 }], | ||||
|                 exclude: /node_modules/ | ||||
|             }, | ||||
|         ] | ||||
|     }, | ||||
|     plugins: [ | ||||
|         // Copy "public" to "dist" | ||||
|         new CopyWebpackPlugin({ | ||||
|             patterns: [{ | ||||
|                 from: 'public', | ||||
|                 to: '.', | ||||
|                 toType: 'dir', | ||||
|                 globOptions: { | ||||
|                     gitignore: true, | ||||
|                     ignore: [path.resolve(__dirname, 'public/index.html').replace(/\\/g, '/')] | ||||
|                 }, | ||||
|                 noErrorOnMissing: true | ||||
|             }] | ||||
|         }), | ||||
|         // Auto add <script> to "index.html" | ||||
|         new HtmlWebpackPlugin({ | ||||
|             template: 'public/index.html' | ||||
|         }), | ||||
|     ], | ||||
|     devtool: isProduction ? false : 'inline-source-map' | ||||
| } | ||||
		Reference in New Issue
	
	Block a user