├── .gitattributes
├── .gitignore
├── LICENSE
├── MicrofrontendTemplate
├── .gitignore
├── .template.config
│ └── template.json
├── Microfrontend.sln
├── Portal
│ ├── Portal.csproj
│ ├── Properties
│ │ └── launchSettings.json
│ ├── appsettings.json
│ ├── client
│ │ ├── auth.js
│ │ ├── components
│ │ │ ├── App.js
│ │ │ ├── Landing.js
│ │ │ ├── Login.js
│ │ │ └── Portal.js
│ │ ├── index.js
│ │ └── loader.js
│ ├── package-lock.json
│ ├── package.json
│ ├── run.bat
│ ├── server
│ │ ├── Controllers
│ │ │ └── LoginController.cs
│ │ ├── Program.cs
│ │ └── Startup.cs
│ ├── tempkey.rsa
│ ├── webpack.config.js
│ └── wwwroot
│ │ ├── 404.html
│ │ ├── index.html
│ │ └── landing.jpg
├── README.md
├── ReactDashboardApp
│ ├── Properties
│ │ └── launchSettings.json
│ ├── README.md
│ ├── ReactDashboardApp.csproj
│ ├── appsettings.json
│ ├── client
│ │ ├── components
│ │ │ ├── ActivitiesCard.js
│ │ │ ├── Dashboard.js
│ │ │ └── InfoCard.js
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── run.bat
│ ├── server
│ │ ├── Program.cs
│ │ ├── Services
│ │ │ └── MockLiveDataService.cs
│ │ ├── Startup.cs
│ │ └── ViewModels
│ │ │ └── Dashboard.cs
│ ├── webpack.config.js
│ └── wwwroot
│ │ └── index.html
├── ReactFormApp
│ ├── Properties
│ │ └── launchSettings.json
│ ├── README.md
│ ├── ReactFormApp.csproj
│ ├── appsettings.json
│ ├── client
│ │ ├── components
│ │ │ ├── AddressForm.js
│ │ │ ├── BasicInfoForm.js
│ │ │ ├── Form.js
│ │ │ └── NewCustomerDialog.js
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── run.bat
│ ├── server
│ │ ├── Program.cs
│ │ ├── Services
│ │ │ ├── Customer.cs
│ │ │ ├── CustomerFormData.cs
│ │ │ └── CustomerRepository.cs
│ │ ├── Startup.cs
│ │ └── ViewModels
│ │ │ ├── AddressForm.cs
│ │ │ ├── CustomerForm.cs
│ │ │ ├── NewCustomerForm.cs
│ │ │ ├── PersonForm.cs
│ │ │ └── PhoneForm.cs
│ ├── webpack.config.js
│ └── wwwroot
│ │ └── index.html
├── ReactTodoApp
│ ├── Properties
│ │ └── launchSettings.json
│ ├── README.md
│ ├── ReactTodoApp.csproj
│ ├── appsettings.json
│ ├── client
│ │ ├── components
│ │ │ └── TodoList.js
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── run.bat
│ ├── server
│ │ ├── Program.cs
│ │ ├── Startup.cs
│ │ └── ViewModels
│ │ │ └── TodoList.cs
│ ├── webpack.config.js
│ └── wwwroot
│ │ └── index.html
├── Shared
│ ├── IdentityServer
│ │ ├── IdentityServerClient.cs
│ │ ├── IdentityServerConfig.cs
│ │ └── IdentityServerSettings.cs
│ └── Shared.csproj
├── VueTodoApp
│ ├── Properties
│ │ └── launchSettings.json
│ ├── README.md
│ ├── VueTodoApp.csproj
│ ├── appsettings.json
│ ├── client
│ │ ├── components
│ │ │ └── TodoList.vue
│ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── run.bat
│ ├── server
│ │ ├── Program.cs
│ │ ├── Startup.cs
│ │ └── ViewModels
│ │ │ └── TodoList.cs
│ ├── webpack.config.js
│ └── wwwroot
│ │ └── index.html
├── global.json
├── heroku-deploy
│ ├── Portal
│ │ ├── Dockerfile
│ │ ├── appsettings.json
│ │ ├── build.bat
│ │ ├── deploy.bat
│ │ └── excludedfiles.txt
│ ├── ReactDashboardApp
│ │ ├── Dockerfile
│ │ ├── appsettings.json
│ │ ├── build.bat
│ │ ├── deploy.bat
│ │ └── excludedfiles.txt
│ └── nginx
│ │ ├── Dockerfile
│ │ ├── deploy.bat
│ │ └── nginx.conf
├── nginx
│ ├── .gitignore
│ ├── conf
│ │ ├── fastcgi.conf
│ │ ├── fastcgi_params
│ │ ├── koi-utf
│ │ ├── koi-win
│ │ ├── mime.types
│ │ ├── nginx.conf
│ │ ├── scgi_params
│ │ ├── uwsgi_params
│ │ └── win-utf
│ ├── contrib
│ │ ├── README
│ │ ├── geo2nginx.pl
│ │ ├── unicode2nginx
│ │ │ ├── koi-utf
│ │ │ ├── unicode-to-nginx.pl
│ │ │ └── win-utf
│ │ └── vim
│ │ │ ├── ftdetect
│ │ │ └── nginx.vim
│ │ │ ├── ftplugin
│ │ │ └── nginx.vim
│ │ │ ├── indent
│ │ │ └── nginx.vim
│ │ │ └── syntax
│ │ │ └── nginx.vim
│ ├── docs
│ │ ├── CHANGES
│ │ ├── CHANGES.ru
│ │ ├── LICENSE
│ │ ├── OpenSSL.LICENSE
│ │ ├── PCRE.LICENCE
│ │ ├── README
│ │ └── zlib.LICENSE
│ ├── html
│ │ ├── 50x.html
│ │ └── index.html
│ ├── nginx.exe
│ ├── run.bat
│ └── stop.bat
├── package-lock.json
└── run.bat
├── README.md
├── ReactTemplate
├── README.md
├── content
│ ├── .gitignore
│ ├── .template.config
│ │ └── template.json
│ ├── client
│ │ ├── app.tsx
│ │ ├── auth.ts
│ │ ├── components
│ │ │ ├── BasePage.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── dashboard
│ │ │ │ ├── InfoBox.tsx
│ │ │ │ ├── RecentActivities.tsx
│ │ │ │ ├── ServerUsage.tsx
│ │ │ │ ├── Traffic.tsx
│ │ │ │ └── Utilization.tsx
│ │ │ └── table
│ │ │ │ ├── InlineEdit.tsx
│ │ │ │ └── Pagination.tsx
│ │ ├── favicon.ico
│ │ ├── images
│ │ │ └── material_bg.png
│ │ ├── routes.tsx
│ │ ├── styles
│ │ │ ├── app.css
│ │ │ ├── styles.ts
│ │ │ └── theme-default.ts
│ │ └── views
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── Dashboard.tsx
│ │ │ ├── FormPage.tsx
│ │ │ ├── LoginPage.tsx
│ │ │ └── TablePage.tsx
│ ├── package-lock.json
│ ├── package.json
│ ├── projectName.csproj
│ ├── projectName.sln
│ ├── server
│ │ ├── AuthServer.cs
│ │ ├── Program.cs
│ │ ├── Services
│ │ │ ├── EmployeeService.cs
│ │ │ ├── MockLiveDataService.cs
│ │ │ └── employees.json
│ │ └── ViewModels
│ │ │ ├── AppLayout.cs
│ │ │ ├── Dashboard.cs
│ │ │ ├── Form.cs
│ │ │ └── Table.cs
│ ├── tsconfig.json
│ ├── webpack.config.js
│ └── wwwroot
│ │ ├── 404.html
│ │ ├── images
│ │ └── avatar.png
│ │ └── index.html
├── screenshot.gif
└── template.nuspec
└── _archive
├── HelloWorld
├── HelloWorld.cs
├── HelloWorld.csproj
├── HelloWorld.sln
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── package-lock.json
├── package.json
├── src
│ ├── HelloWorld.jsx
│ └── app.js
├── webpack.config.js
└── wwwroot
│ ├── bundle.js
│ └── index.html
├── LiveChart
├── IISPublishingSteps.md
├── LiveChart.cs
├── LiveChart.csproj
├── LiveChart.sln
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── compilerconfig.json
├── compilerconfig.json.defaults
├── package-lock.json
├── package.json
├── src
│ ├── LiveChart.jsx
│ └── app.js
├── webpack.config.js
└── wwwroot
│ ├── bundle.js
│ └── index.html
├── README.md
├── ReactTemplateTS
├── .editorconfig
├── README.md
├── content
│ ├── .babelrc
│ ├── .gitignore
│ ├── .template.config
│ │ └── template.json
│ ├── client
│ │ ├── app.tsx
│ │ ├── auth.tsx
│ │ ├── components
│ │ │ ├── BasePage.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── dashboard
│ │ │ │ ├── InfoBox.tsx
│ │ │ │ ├── RecentActivities.tsx
│ │ │ │ ├── ServerUsage.tsx
│ │ │ │ ├── Traffic.tsx
│ │ │ │ └── Utilization.tsx
│ │ │ └── table
│ │ │ │ ├── InlineEdit.tsx
│ │ │ │ └── Pagination.tsx
│ │ ├── favicon.ico
│ │ ├── images
│ │ │ └── material_bg.png
│ │ ├── routes.tsx
│ │ ├── styles
│ │ │ ├── app.css
│ │ │ ├── styles.ts
│ │ │ └── theme-default.ts
│ │ └── views
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── Dashboard.tsx
│ │ │ ├── FormPage.tsx
│ │ │ ├── LoginPage.tsx
│ │ │ └── TablePage.tsx
│ ├── dotnetify_react_template.csproj
│ ├── dotnetify_react_template.sln
│ ├── images.d.ts
│ ├── package.json
│ ├── server
│ │ ├── AuthServer.cs
│ │ ├── Program.cs
│ │ ├── Services
│ │ │ ├── EmployeeService.cs
│ │ │ ├── MockLiveDataService.cs
│ │ │ └── employees.json
│ │ ├── Startup.cs
│ │ └── ViewModels
│ │ │ ├── AppLayout.cs
│ │ │ ├── Dashboard.cs
│ │ │ ├── Form.cs
│ │ │ └── Table.cs
│ ├── tsconfig.json
│ ├── webpack.config.js
│ └── wwwroot
│ │ └── index.html
├── screenshot.gif
└── template.nuspec
└── Routing
├── DotNetify.ServerSideRender.cs
├── Program.cs
├── Properties
└── launchSettings.json
├── Routing.csproj
├── Routing.sln
├── Startup.cs
├── ViewModels
├── Index.cs
├── Page1.cs
└── Page2.cs
├── compilerconfig.json
├── compilerconfig.json.defaults
├── package.json
├── src
├── Index.js
├── Index.jsx
├── Index.min.js
├── app.js
└── app.server.js
├── webpack.config.js
└── wwwroot
├── Index.html
├── Page1.js
├── Page1.jsx
├── Page1.min.js
├── Page2.js
├── Page2.jsx
├── Page2.min.js
├── Page3.html
├── bundle.js
└── favicon.ico
/MicrofrontendTemplate/.template.config/template.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsuryd/dotNetify-react-template/c1f80d7cdb751069eaa5f7ed7e74680997be1eb9/MicrofrontendTemplate/.template.config/template.json
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/Portal.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp3.1
4 | Portal
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | %(DistFiles.Identity)
39 | PreserveNewest
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5000/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "dev": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:5000/"
25 | },
26 | "prod": {
27 | "commandName": "Project",
28 | "launchBrowser": true,
29 | "environmentVariables": {
30 | "ASPNETCORE_ENVIRONMENT": "Production"
31 | },
32 | "applicationUrl": "http://localhost:5000/"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://localhost:5000"
4 | }
5 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/client/auth.js:
--------------------------------------------------------------------------------
1 | const url = '/api/login';
2 |
3 | export const signIn = (username, password) => {
4 | return fetch(url, {
5 | method: 'post',
6 | body: 'username=' + username + '&password=' + password,
7 | headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }
8 | })
9 | .then(response => {
10 | if (!response.ok) throw new Error(response.status);
11 | return response.json();
12 | })
13 | .then(token => window.sessionStorage.setItem('access_token', token.access_token));
14 | };
15 |
16 | export const signOut = () => {
17 | window.sessionStorage.removeItem('access_token');
18 | window.location.href = '/';
19 | };
20 |
21 | export const validateToken = token => {
22 | return fetch(url + '/validate', {
23 | method: 'post',
24 | body: 'token=' + token,
25 | headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }
26 | }).then(response => response.ok);
27 | };
28 |
29 | export const getAccessToken = () => {
30 | return window.sessionStorage.getItem('access_token');
31 | };
32 |
33 | export const getAuthHeaders = _ => ({
34 | headers: { Authorization: 'Bearer ' + getAccessToken() },
35 | exceptionHandler: _ => signOut()
36 | });
37 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/client/components/App.js:
--------------------------------------------------------------------------------
1 | import { Fragment, useState, useEffect } from 'react';
2 | import { getAccessToken, validateToken } from '../auth';
3 | import Portal from './Portal';
4 | import Landing from './Landing';
5 |
6 | export const CompanyLogo = styled.a`
7 | display: flex;
8 | align-items: center;
9 | margin-left: 1rem;
10 | background-image: url(https://dotnetify.net/content/images/dotnetify-logo.png);
11 | background-size: 100% 100%;
12 | width: 200px;
13 | height: 39px;
14 | `;
15 |
16 | const App = _ => {
17 | const [ accessToken ] = useState(getAccessToken());
18 | const [ validatingToken, setValidatingToken ] = useState(null);
19 | const [ loggedIn, setLoggedIn ] = useState(false);
20 |
21 | useEffect(() => {
22 | if (validatingToken === null) {
23 | setValidatingToken(!!accessToken);
24 | if (accessToken) {
25 | validateToken(accessToken).then(valid => {
26 | setLoggedIn(valid);
27 | setValidatingToken(false);
28 | });
29 | }
30 | }
31 | });
32 |
33 | if (validatingToken !== false) return ;
34 | return loggedIn ? : setLoggedIn(true)} />;
35 | };
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/client/components/Login.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { signIn } from '../auth';
3 | import { Alert, Button, Form, Panel, PasswordField, TextField, VMContext, withTheme } from 'dotnetify-elements';
4 |
5 | const outerPanelCss = `
6 | width: 25rem;
7 | padding: 3rem;
8 | background-color: #f5f5f5;
9 | border-radius: 5px;
10 | box-shadow: 0 20px 40px -20px rgba(0,0,0,.5);
11 | `;
12 |
13 | const Login = ({ onAuthenticated }) => {
14 | const [ loginError, setLoginError ] = useState();
15 |
16 | const handleLogin = ({ User, Password }) => {
17 | setLoginError(null);
18 | signIn(User, Password)
19 | .then(_ => {
20 | onAuthenticated();
21 | })
22 | .catch(error => {
23 | if (error.message == '401') setLoginError('Invalid username and/or password');
24 | else setLoginError(`Unexpected error: ${error.message}`);
25 | });
26 | };
27 |
28 | return (
29 |
30 |
31 | Sign In To Get Started
32 |
40 |
41 |
42 | );
43 | };
44 |
45 | window.LoginVM = {
46 | onConnect() {
47 | return {
48 | User: 'guest',
49 | User__validation: [
50 | {
51 | Type: 'Required',
52 | Message: 'Username is required',
53 | Category: 'Error'
54 | }
55 | ],
56 | Password: '',
57 | Password__attr: { Placeholder: 'Type dotnetify' },
58 | Password__validation: [
59 | {
60 | Type: 'Required',
61 | Message: 'Password is required',
62 | Category: 'Error'
63 | }
64 | ]
65 | };
66 | }
67 | };
68 |
69 | export default withTheme(Login);
70 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/client/components/Portal.js:
--------------------------------------------------------------------------------
1 | import {
2 | Main,
3 | Header,
4 | Nav,
5 | NavDrawerButton,
6 | NavMenu,
7 | NavMenuTarget,
8 | Button,
9 | Panel,
10 | Section,
11 | VMContext,
12 | withTheme
13 | } from 'dotnetify-elements';
14 | import { CompanyLogo } from './App';
15 | import { signOut } from '../auth';
16 |
17 | const Portal = _ => (
18 |
19 |
20 |
24 |
32 |
35 |
36 |
37 | );
38 |
39 | window.PortalVM = new class {
40 | apps = [];
41 | initialState = { NavMenu: [] };
42 |
43 | onConnect() {
44 | return this.initialState;
45 | }
46 |
47 | addApp(newApp) {
48 | if (this.apps.find(x => x.id === newApp.id)) return;
49 |
50 | this.apps.push(newApp);
51 |
52 | // Set the root component to global window variable to be discovered by dotNetify routing.
53 | // Note that the component can be React component, Vue component, or web component.
54 | window[newApp.id] = newApp.rootComponent;
55 |
56 | const homeTemplate = this.apps.length > 0 ? { Id: 'Home', UrlPattern: '', ViewUrl: this.apps[0].id } : {};
57 | const templates = this.apps.map(app => ({ Id: app.id, UrlPattern: app.routePath, ViewUrl: app.id }));
58 | const NavMenu = this.apps.map(app => ({ Route: { TemplateId: app.id, Path: app.routePath }, Label: app.label }));
59 | const state = {
60 | RoutingState: {
61 | Templates: [ homeTemplate, ...templates ],
62 | Root: '/'
63 | },
64 | NavMenu
65 | };
66 |
67 | if (this.$pushUpdate) this.$pushUpdate(state);
68 | else this.initialState = state;
69 | }
70 | }();
71 |
72 | export function updatePortal(app) {
73 | window.PortalVM.addApp(app);
74 | }
75 | export default withTheme(Portal);
76 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/client/index.js:
--------------------------------------------------------------------------------
1 | import { createWebComponent } from 'dotnetify-elements/web-components/Core';
2 | import loader from './loader';
3 | import App from './components/App';
4 |
5 | let useApiGateway = process.env.NODE_ENV !== 'development';
6 |
7 | loader(
8 | [
9 | {
10 | id: 'react-dashboard-app',
11 | label: 'React Dashboard',
12 | routePath: 'dashboard',
13 | baseUrl: useApiGateway ? 'http://localhost:8080/app1' : 'http://localhost:5060',
14 | moduleUrl: '/dist/app.js'
15 | },
16 | {
17 | id: 'react-form-app',
18 | label: 'React Form',
19 | routePath: 'form',
20 | baseUrl: useApiGateway ? 'http://localhost:8080/app2' : 'http://localhost:5070',
21 | moduleUrl: '/dist/app.js'
22 | },
23 | {
24 | id: 'react-todo-app',
25 | label: 'React Todo',
26 | routePath: 'react-todo',
27 | baseUrl: useApiGateway ? 'http://localhost:8080/app3' : 'http://localhost:5010',
28 | moduleUrl: '/dist/app.js'
29 | },
30 | {
31 | id: 'vue-todo-app',
32 | label: 'Vue Todo',
33 | routePath: 'vue-todo',
34 | baseUrl: useApiGateway ? 'http://localhost:8080/app4' : 'http://localhost:5020',
35 | moduleUrl: '/dist/app.js'
36 | }
37 | ],
38 | // External dependencies from script tags.
39 | ['dotnetify', 'dotNetifyElements', 'styled']
40 | );
41 |
42 | createWebComponent(App, 'my-portal');
43 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/client/loader.js:
--------------------------------------------------------------------------------
1 | import dotnetify from 'dotnetify';
2 | import { getAccessToken } from './auth';
3 | import { updatePortal } from './components/Portal';
4 |
5 | //dotnetify.debug = true;
6 |
7 | export default (apps, externalDeps) => {
8 | // Intercept initial view model connection to override which hub server it should connect with.
9 | // ***IMPORTANT***: Each app module would need to set the 'appId' option on its VM connect.
10 | // For example: dotnetify.react.connect("MyVM", { appId: 'my-app' })
11 | dotnetify.connectHandler = vmConnectArgs => {
12 | const appId = vmConnectArgs.options && vmConnectArgs.options.appId;
13 | if (!appId) {
14 | console.error(`'${vmConnectArgs.vmId}' needs 'appId' option to participate in the Portal.`);
15 | return;
16 | }
17 |
18 | const app = apps.find(x => x.id === appId);
19 | if (app) {
20 | app.hub = app.hub || dotnetify.createHub(app.baseUrl);
21 | return {
22 | ...vmConnectArgs,
23 | hub: app.hub,
24 | options: { ...vmConnectArgs.options, headers: { Authorization: 'Bearer ' + getAccessToken() } }
25 | };
26 | }
27 | };
28 |
29 | // Register the external dependencies from the script tags to SystemJS.
30 | externalDeps.forEach(x => SystemJS.set(x, SystemJS.newModule({ ...window[x] })));
31 |
32 | // Use SystemJS to import the app modules.
33 | function importApp(app) {
34 | const appUrl = app.baseUrl + app.moduleUrl;
35 | return SystemJS.import(appUrl)
36 | .then(module => {
37 | if (module.default) updatePortal({ ...app, rootComponent: module.default });
38 | })
39 | .catch(err => {
40 | SystemJS.delete(appUrl);
41 | console.error(`${err}. Retrying in 5 seconds...`);
42 | setTimeout(() => importApp(app), 5000);
43 | });
44 | }
45 |
46 | apps.map(app => importApp(app));
47 | };
48 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "name": "portal",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "prod": "webpack --mode production"
8 | },
9 | "babel": {
10 | "presets": [
11 | "env",
12 | "react"
13 | ],
14 | "plugins": [
15 | "babel-plugin-styled-components",
16 | "babel-plugin-transform-class-properties",
17 | "babel-plugin-transform-object-rest-spread"
18 | ]
19 | },
20 | "dependencies": {
21 | "dotnetify": "^4.0.0",
22 | "dotnetify-elements": "^1.1.0",
23 | "react": "^16.9.0",
24 | "react-dom": "^16.9.0",
25 | "styled-components": "^4.3.2",
26 | "systemjs": "~0.21.6"
27 | },
28 | "devDependencies": {
29 | "aspnet-webpack": "~3.0.0",
30 | "babel-core": "~6.26.3",
31 | "babel-loader": "~7.1.4",
32 | "babel-plugin-styled-components": "~1.5.1",
33 | "babel-plugin-transform-class-properties": "~6.24.1",
34 | "babel-plugin-transform-object-rest-spread": "~6.26.0",
35 | "babel-preset-env": "~1.7.0",
36 | "babel-preset-react": "~6.24.1",
37 | "copy-webpack-plugin": "^5.0.4",
38 | "css-loader": "~0.28.11",
39 | "file-loader": "~1.1.11",
40 | "style-loader": "~0.21.0",
41 | "url-loader": "~1.0.1",
42 | "webpack": "~4.12.0",
43 | "webpack-bundle-analyzer": "^3.0.3",
44 | "webpack-cli": "~3.0.8",
45 | "webpack-dev-middleware": "~3.1.3",
46 | "webpack-hot-middleware": "~2.22.2"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if not exist node_modules call npm i
3 | if "%1" == "prod" goto :prod
4 | call npm run build
5 | dotnet run
6 | exit
7 |
8 | :prod
9 | call npm run prod
10 | dotnet run --launch-profile prod
11 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/server/Controllers/LoginController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.Extensions.Options;
3 | using Shared;
4 | using System;
5 | using System.Net.Http;
6 | using System.Threading.Tasks;
7 |
8 | namespace Portal
9 | {
10 | [Route("api/[controller]")]
11 | [ApiController]
12 | public class LoginController : ControllerBase
13 | {
14 | private readonly IHttpClientFactory _httpClientFactory;
15 | private readonly IdentityServerSettings _identityServerSettings;
16 |
17 | public LoginController(IHttpClientFactory httpClientFactory, IOptions identityServerOptions)
18 | {
19 | _httpClientFactory = httpClientFactory;
20 | _identityServerSettings = identityServerOptions.Value;
21 | }
22 |
23 | [HttpPost]
24 | public async Task
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | DotNetify Micro-Frontend Demo
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Portal/wwwroot/landing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsuryd/dotNetify-react-template/c1f80d7cdb751069eaa5f7ed7e74680997be1eb9/MicrofrontendTemplate/Portal/wwwroot/landing.jpg
--------------------------------------------------------------------------------
/MicrofrontendTemplate/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## DotNetify - Micro-Frontend Demo
4 |
5 | ### How to Run
6 |
7 | If on Windows, type `run` from the command prompt to start the portal and the nginx API gateway. Wait until the portal app started, then type `run apps` to start all the apps. Alternatively, you can start them one at a time by going to an app's folder and type `run prod`. Open the website at http://localhost:8080.
8 |
9 | If not on Windows, you can't use the Windows-based nginx from this repo. Either install one for your system and copy the configuration `conf/nginx.conf`, or just run the portal and the apps in development mode below.
10 |
11 | ### Running In Dev Mode
12 |
13 | Go the the Portal folder and type `npm i` followed by `dotnet run`. Then go to each app's folder and do the same thing. They will start in development mode. Open the website at http://localhost:5000.
14 |
15 | In development mode, authentication is disabled so you can run the app as stand-alone, and hot module reload is also enabled. The app's port is specified in `Properties/launchsettings.json`.
16 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5060/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "dev": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:5060/"
25 | },
26 | "prod": {
27 | "commandName": "Project",
28 | "launchBrowser": true,
29 | "environmentVariables": {
30 | "ASPNETCORE_ENVIRONMENT": "Production"
31 | },
32 | "applicationUrl": "http://localhost:5060/"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/README.md:
--------------------------------------------------------------------------------
1 | ## How to run:
2 |
3 | Start the Portal first (app requires an auth server), then execute `run.bat [dev|prod]`.
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://localhost:5000"
4 | }
5 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/client/components/ActivitiesCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { Card, Cell, Element, Panel } from 'dotnetify-elements';
4 |
5 | const panelCss = `
6 | overflow-x: hidden;
7 | .cell { border: none; }
8 | .cell-body { padding: .5rem 0 }
9 | `;
10 |
11 | const statusColors = [ '', 'silver', 'limegreen', 'red', 'gray', 'orange' ];
12 | const userIconColors = [ '#00ce6f', '#a95df0', '#2ea7eb' ];
13 |
14 | const UserIcon = styled.span`
15 | width: 25px;
16 | height: 25px;
17 | border-radius: 50%;
18 | color: white;
19 | background: ${props => props.color};
20 | font-weight: bold;
21 | margin-right: 1rem;
22 | text-align: center;
23 | `;
24 |
25 | const StatusIcon = styled.span`
26 | height: 14px;
27 | width: 14px;
28 | margin-left: 1rem;
29 | background-color: ${props => statusColors[props.status]};
30 | border-radius: 50%;
31 | display: inline-block;
32 | `;
33 |
34 | const Activity = ({ person }) => {
35 | const initial = person.PersonName[0].toUpperCase();
36 | const iconColor = userIconColors[initial.charCodeAt(0) % 3];
37 | return (
38 |
39 |
40 | {initial}
41 | {person.PersonName}
42 | |
43 |
44 | {person.Status}
45 |
46 | |
47 |
48 | );
49 | };
50 |
51 | export default class ActivitiesCard extends Element {
52 | render() {
53 | const activities = this.value || [];
54 | return (
55 |
56 | Activities
57 | {activities.map((person, idx) => )}
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/client/components/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card, Frame, Panel, VMContext, withTheme } from 'dotnetify-elements';
3 | import { BarChart, LineChart, PieChart } from 'dotnetify-elements';
4 | import InfoCard from './InfoCard';
5 | import ActivitiesCard from './ActivitiesCard';
6 |
7 | const infoPanelCss = `
8 | flex: 1 1 20%;
9 | @media (max-width: 1280px) { flex: 1 1 40%; }
10 | @media (max-width: 880px) { flex: 1 1 100%; }
11 | `;
12 |
13 | /* You can access the 'vm' object from VMContext by handling the 'onConnected' event. */
14 | const handleConnected = (vm, initialState) => console.log(vm, initialState);
15 |
16 | const Dashboard = _ => (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Network Traffic
38 |
39 |
40 |
41 | Utilization
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Server Usage
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 |
62 | export default withTheme(Dashboard);
63 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/client/components/InfoCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 | import { Card, CardImage, Element } from 'dotnetify-elements';
4 |
5 | const InfoIcon = styled.i.attrs(props => ({
6 | className: 'material-icons'
7 | }))`
8 | font-size: 3rem;
9 | padding: 1.5rem;
10 | color: white;
11 | background: ${props => props.color};
12 | opacity: .8;
13 | `;
14 |
15 | const cardCss = `
16 | .card-body { padding: .5rem 1.5rem }
17 | h3 { font: 600 2rem Helvetica; }
18 | @media (max-width: 1550px) and (min-width: 1280px) {
19 | h3 { font-size: 1.25rem }
20 | }
21 | `;
22 |
23 | export default class InfoCard extends Element {
24 | render() {
25 | const { color, icon, label } = this.attrs;
26 | return (
27 |
28 |
29 | {icon}
30 |
31 |
32 | {this.value}
33 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/client/index.js:
--------------------------------------------------------------------------------
1 | import { createWebComponent } from 'dotnetify-elements/web-components/Core';
2 | import Dashboard from './components/Dashboard';
3 |
4 | const elementName = 'react-dashboard-app';
5 | createWebComponent(Dashboard, elementName);
6 |
7 | export default document.createElement(elementName);
8 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "name": "react-dashboard-app",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "prod": "webpack --mode production"
8 | },
9 | "babel": {
10 | "presets": [
11 | "env",
12 | "react"
13 | ],
14 | "plugins": [
15 | "babel-plugin-styled-components",
16 | "babel-plugin-transform-class-properties",
17 | "babel-plugin-transform-object-rest-spread"
18 | ]
19 | },
20 | "dependencies": {
21 | "dotnetify": "^4.0.0",
22 | "dotnetify-elements": "^1.1.0",
23 | "react": "^16.9.0",
24 | "react-dom": "^16.9.0",
25 | "styled-components": "^4.3.2"
26 | },
27 | "devDependencies": {
28 | "aspnet-webpack": "~3.0.0",
29 | "babel-core": "~6.26.3",
30 | "babel-loader": "~7.1.4",
31 | "babel-plugin-styled-components": "~1.5.1",
32 | "babel-plugin-transform-class-properties": "~6.24.1",
33 | "babel-plugin-transform-object-rest-spread": "~6.26.0",
34 | "babel-preset-env": "~1.7.0",
35 | "babel-preset-react": "~6.24.1",
36 | "copy-webpack-plugin": "^5.0.4",
37 | "css-loader": "~0.28.11",
38 | "file-loader": "~1.1.11",
39 | "style-loader": "~0.21.0",
40 | "url-loader": "~1.0.1",
41 | "webpack": "~4.12.0",
42 | "webpack-bundle-analyzer": "^3.4.1",
43 | "webpack-cli": "~3.0.8",
44 | "webpack-dev-middleware": "~3.1.3",
45 | "webpack-hot-middleware": "~2.22.2"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if not exist node_modules call npm i
3 | if "%1" == "prod" goto :prod
4 | call npm run build
5 | dotnet run
6 | exit
7 |
8 | :prod
9 | call npm run prod
10 | dotnet run --launch-profile prod
11 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/server/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace ReactDashboardApp
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | BuildWebHost(args).Run();
11 | }
12 |
13 | public static IWebHost BuildWebHost(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup()
16 | .Build();
17 | }
18 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const CopyPlugin = require('copy-webpack-plugin');
5 |
6 | module.exports = {
7 | mode: 'development',
8 | entry: {
9 | app: './client/index.js'
10 | },
11 | output: {
12 | filename: '[name].js',
13 | path: __dirname + '/wwwroot/dist',
14 | publicPath: '/dist/',
15 | libraryTarget: 'umd'
16 | },
17 | resolve: {
18 | modules: [ 'client', 'node_modules' ],
19 | extensions: [ '.js', '.jsx' ]
20 | },
21 | module: {
22 | rules: [
23 | { test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ },
24 | { test: /\.css$/, use: [ 'css-loader?minimize' ] },
25 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader' }
26 | ]
27 | },
28 | externals: {
29 | dotnetify: 'dotnetify',
30 | 'dotnetify-elements': 'dotNetifyElements',
31 | 'styled-components': 'styled'
32 | },
33 | devtool: 'source-map',
34 | plugins: [
35 | new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/),
36 | new CopyPlugin([
37 | { from: 'node_modules/dotnetify/dist/dotnetify-react.min.js' },
38 | { from: 'node_modules/dotnetify-elements/lib/dotnetify-elements.bundle.js' },
39 | { from: 'node_modules/styled-components/dist/styled-components.min.js' }
40 | ])
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactDashboardApp/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React Dashboard App
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5070/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "dev": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:5070/"
25 | },
26 | "prod": {
27 | "commandName": "Project",
28 | "launchBrowser": true,
29 | "environmentVariables": {
30 | "ASPNETCORE_ENVIRONMENT": "Production"
31 | },
32 | "applicationUrl": "http://localhost:5070/"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/README.md:
--------------------------------------------------------------------------------
1 | ## How to run:
2 |
3 | Start the Portal first (app requires an auth server), then execute `run.bat [dev|prod]`.
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://localhost:5000"
4 | }
5 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/client/components/AddressForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Cell, DropdownList, Form, NumberField, Panel, TextField, VMContext } from 'dotnetify-elements';
3 |
4 | const AddressForm = () => (
5 |
6 |
7 |
8 |
17 | s
18 | |
19 |
20 | );
21 |
22 | export default AddressForm;
23 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/client/components/BasicInfoForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Cell, DropdownList, Form, Panel, TextField, VMContext } from 'dotnetify-elements';
3 |
4 | const BasicInfoForm = () => (
5 |
6 |
7 |
8 |
9 |
19 |
20 | |
21 |
22 |
23 |
31 |
32 | |
33 |
34 |
35 | );
36 |
37 | export default BasicInfoForm;
38 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/client/components/Form.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BasicInfoForm from './BasicInfoForm';
3 | import AddressForm from './AddressForm';
4 | import NewCustomerDialog from './NewCustomerDialog';
5 | import { Button, DataGrid, Form, Frame, Panel, Tab, TabItem, VMContext, withTheme } from 'dotnetify-elements';
6 |
7 | class CustomerForm extends React.Component {
8 | state = { editable: false, edit: false, openDialog: false };
9 |
10 | handleSelect = value => this.setState({ editable: value ? true : false });
11 | toggleEdit = _ => this.setState({ edit: !this.state.edit });
12 | toggleDialog = _ => this.setState({ openDialog: !this.state.openDialog });
13 |
14 | render() {
15 | const { editable, edit, openDialog } = this.state;
16 | const canEdit = editable && !edit;
17 | return (
18 |
19 |
20 |
21 |
45 |
46 |
47 |
48 | );
49 | }
50 | }
51 |
52 | export default withTheme(CustomerForm);
53 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/client/index.js:
--------------------------------------------------------------------------------
1 | import { createWebComponent } from 'dotnetify-elements/web-components/Core';
2 | import Form from './components/Form';
3 |
4 | const elementName = 'react-form-app';
5 | createWebComponent(Form, elementName);
6 |
7 | export default document.createElement(elementName);
8 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "name": "react-form-app",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "prod": "webpack --mode production"
8 | },
9 | "babel": {
10 | "presets": [
11 | "env",
12 | "react"
13 | ],
14 | "plugins": [
15 | "babel-plugin-styled-components",
16 | "babel-plugin-transform-class-properties",
17 | "babel-plugin-transform-object-rest-spread"
18 | ]
19 | },
20 | "dependencies": {
21 | "dotnetify": "^4.0.0",
22 | "dotnetify-elements": "^1.1.0",
23 | "react": "^16.9.0",
24 | "react-dom": "^16.9.0",
25 | "styled-components": "^4.3.2"
26 | },
27 | "devDependencies": {
28 | "aspnet-webpack": "~3.0.0",
29 | "babel-core": "~6.26.3",
30 | "babel-loader": "~7.1.4",
31 | "babel-plugin-styled-components": "~1.5.1",
32 | "babel-plugin-transform-class-properties": "~6.24.1",
33 | "babel-plugin-transform-object-rest-spread": "~6.26.0",
34 | "babel-preset-env": "~1.7.0",
35 | "babel-preset-react": "~6.24.1",
36 | "copy-webpack-plugin": "^5.0.4",
37 | "css-loader": "~0.28.11",
38 | "file-loader": "~1.1.11",
39 | "style-loader": "~0.21.0",
40 | "url-loader": "~1.0.1",
41 | "webpack": "~4.12.0",
42 | "webpack-bundle-analyzer": "^3.4.1",
43 | "webpack-cli": "~3.0.8",
44 | "webpack-dev-middleware": "~3.1.3",
45 | "webpack-hot-middleware": "~2.22.2"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if not exist node_modules call npm i
3 | if "%1" == "prod" goto :prod
4 | call npm run build
5 | dotnet run
6 | exit
7 |
8 | :prod
9 | call npm run prod
10 | dotnet run --launch-profile prod
11 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/server/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace ReactFormApp
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | BuildWebHost(args).Run();
11 | }
12 |
13 | public static IWebHost BuildWebHost(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup()
16 | .Build();
17 | }
18 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/server/Services/CustomerFormData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace ReactFormApp
4 | {
5 | using StringDictionary = Dictionary;
6 |
7 | public class CustomerFormData
8 | {
9 | public StringDictionary Person { get; set; }
10 | public StringDictionary Phone { get; set; }
11 | public StringDictionary Address { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/server/ViewModels/AddressForm.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Reactive.Linq;
3 | using DotNetify;
4 | using DotNetify.Elements;
5 | using DotNetify.Security;
6 |
7 | namespace ReactFormApp
8 | {
9 | [Authorize]
10 | public class AddressForm : BaseVM
11 | {
12 | public ReactiveProperty Customer { get; } = new ReactiveProperty();
13 |
14 | public AddressForm()
15 | {
16 | AddProperty(nameof(AddressInfo.Address1))
17 | .WithAttribute(new TextFieldAttribute { Label = "Address 1:" })
18 | .SubscribeTo(Customer.Select(x => x.Address.Address1));
19 |
20 | AddProperty(nameof(AddressInfo.Address2))
21 | .WithAttribute(new TextFieldAttribute { Label = "Address 2:" })
22 | .SubscribeTo(Customer.Select(x => x.Address.Address2));
23 |
24 | AddProperty(nameof(AddressInfo.City))
25 | .WithAttribute(new TextFieldAttribute { Label = "City:" })
26 | .SubscribeTo(Customer.Select(x => x.Address.City));
27 |
28 | AddProperty(nameof(AddressInfo.State))
29 | .WithAttribute(new DropdownListAttribute
30 | {
31 | Label = "State:",
32 | Options = typeof(State).ToDescriptions()
33 | })
34 | .SubscribeTo(Customer.Select(x => x.Address.State));
35 |
36 | AddProperty(nameof(AddressInfo.ZipCode))
37 | .WithAttribute(new TextFieldAttribute { Label = "Zip Code:" })
38 | .SubscribeTo(Customer.Select(x => x.Address.ZipCode));
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/server/ViewModels/NewCustomerForm.cs:
--------------------------------------------------------------------------------
1 | using DotNetify;
2 | using DotNetify.Elements;
3 | using DotNetify.Security;
4 | using System.Linq;
5 | using System.Reactive.Linq;
6 |
7 | namespace ReactFormApp
8 | {
9 | [Authorize]
10 | public class NewCustomerForm : BaseVM
11 | {
12 | private readonly ICustomerRepository _customerRepository;
13 |
14 | public ReactiveProperty NewCustomer { get; } = new ReactiveProperty();
15 |
16 | public NewCustomerForm(ICustomerRepository customerRepository)
17 | {
18 | _customerRepository = customerRepository;
19 |
20 | AddInternalProperty("Submit")
21 | .SubscribedBy(NewCustomer, formData => Save(formData));
22 | }
23 |
24 | public override void Dispose()
25 | {
26 | base.Dispose();
27 | }
28 |
29 | public Customer Save(CustomerFormData formData)
30 | {
31 | return _customerRepository.Add(formData);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/server/ViewModels/PersonForm.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Reactive.Linq;
3 | using DotNetify;
4 | using DotNetify.Elements;
5 | using DotNetify.Security;
6 |
7 | namespace ReactFormApp
8 | {
9 | [Authorize]
10 | public class PersonForm : BaseVM
11 | {
12 | public ReactiveProperty Customer { get; } = new ReactiveProperty();
13 |
14 | public PersonForm()
15 | {
16 | AddProperty(nameof(NameInfo.FullName))
17 | .WithAttribute(new TextFieldAttribute { Label = "Name:" })
18 | .SubscribeTo(Customer.Select(x => x.Name.FullName));
19 |
20 | AddProperty(nameof(NameInfo.Prefix))
21 | .WithAttribute(new DropdownListAttribute { Label = "Prefix:", Options = typeof(NamePrefix).ToDescriptions() })
22 | .SubscribeTo(Customer.Select(x => x.Name.Prefix));
23 |
24 | AddProperty(nameof(NameInfo.FirstName))
25 | .WithAttribute(new TextFieldAttribute { Label = "First Name:", MaxLength = 35 })
26 | .WithRequiredValidation()
27 | .SubscribeTo(Customer.Select(x => x.Name.FirstName));
28 |
29 | AddProperty(nameof(NameInfo.MiddleName))
30 | .WithAttribute(new TextFieldAttribute { Label = "Middle Name:", MaxLength = 35 })
31 | .SubscribeTo(Customer.Select(x => x.Name.MiddleName));
32 |
33 | AddProperty(nameof(NameInfo.LastName))
34 | .WithAttribute(new TextFieldAttribute { Label = "Last Name:", MaxLength = 35 })
35 | .WithRequiredValidation()
36 | .SubscribeTo(Customer.Select(x => x.Name.LastName));
37 |
38 | AddProperty(nameof(NameInfo.Suffix))
39 | .WithAttribute(new DropdownListAttribute { Label = "Suffix:", Options = typeof(NameSuffix).ToDescriptions() })
40 | .SubscribeTo(Customer.Select(x => x.Name.Suffix));
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/server/ViewModels/PhoneForm.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Reactive.Linq;
3 | using DotNetify;
4 | using DotNetify.Elements;
5 | using DotNetify.Security;
6 |
7 | namespace ReactFormApp
8 | {
9 | [Authorize]
10 | public class PhoneForm : BaseVM
11 | {
12 | public ReactiveProperty Customer { get; } = new ReactiveProperty();
13 |
14 | public PhoneForm()
15 | {
16 | AddProperty(nameof(PhoneInfo.Work))
17 | .WithAttribute(new TextFieldAttribute { Label = "Work:", Mask = "(999) 999-9999" })
18 | .WithPatternValidation(Pattern.USPhoneNumber)
19 | .SubscribeTo(Customer.Select(x => x.Phone.Work));
20 |
21 | AddProperty(nameof(PhoneInfo.Home))
22 | .WithAttribute(new TextFieldAttribute { Label = "Home:", Mask = "(999) 999-9999" })
23 | .WithPatternValidation(Pattern.USPhoneNumber)
24 | .SubscribeTo(Customer.Select(x => x.Phone.Home));
25 |
26 | AddProperty(nameof(PhoneInfo.Mobile))
27 | .WithAttribute(new TextFieldAttribute { Label = "Mobile:", Mask = "(999) 999-9999" })
28 | .WithPatternValidation(Pattern.USPhoneNumber)
29 | .SubscribeTo(Customer.Select(x => x.Phone.Mobile));
30 |
31 | AddProperty(nameof(PhoneInfo.Primary))
32 | .WithAttribute(new DropdownListAttribute { Label = "Primary Phone:", Options = typeof(PrimaryPhone).ToDescriptions() })
33 | .SubscribeTo(Customer.Select(x => x.Phone.Primary));
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const CopyPlugin = require('copy-webpack-plugin');
5 |
6 | module.exports = {
7 | mode: 'development',
8 | entry: {
9 | app: './client/index.js'
10 | },
11 | output: {
12 | filename: '[name].js',
13 | path: __dirname + '/wwwroot/dist',
14 | publicPath: '/dist/',
15 | libraryTarget: 'umd'
16 | },
17 | resolve: {
18 | modules: [ 'client', 'node_modules' ],
19 | extensions: [ '.js', '.jsx' ]
20 | },
21 | module: {
22 | rules: [
23 | { test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ },
24 | { test: /\.css$/, use: [ 'css-loader?minimize' ] },
25 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader' }
26 | ]
27 | },
28 | externals: {
29 | dotnetify: 'dotnetify',
30 | 'dotnetify-elements': 'dotNetifyElements',
31 | 'styled-components': 'styled'
32 | },
33 | devtool: 'source-map',
34 | plugins: [
35 | new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/),
36 | new CopyPlugin([
37 | { from: 'node_modules/dotnetify/dist/dotnetify-react.min.js' },
38 | { from: 'node_modules/dotnetify-elements/lib/dotnetify-elements.bundle.js' },
39 | { from: 'node_modules/styled-components/dist/styled-components.min.js' }
40 | ])
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactFormApp/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React Form App
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5010/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "dev": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:5010/"
25 | },
26 | "prod": {
27 | "commandName": "Project",
28 | "launchBrowser": true,
29 | "environmentVariables": {
30 | "ASPNETCORE_ENVIRONMENT": "Production"
31 | },
32 | "applicationUrl": "http://localhost:5010/"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/README.md:
--------------------------------------------------------------------------------
1 | ## How to run:
2 |
3 | Start the Portal first (app requires an auth server), then execute `run.bat [dev|prod]`.
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/ReactTodoApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | ReactTodoApp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | %(DistFiles.Identity)
41 | PreserveNewest
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://localhost:5000"
4 | }
5 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/client/index.js:
--------------------------------------------------------------------------------
1 | import { createWebComponent } from 'dotnetify-elements/web-components/Core';
2 | import TodoList from './components/TodoList';
3 |
4 | const elementName = 'react-todo-app';
5 | createWebComponent(TodoList, elementName);
6 |
7 | export default document.createElement(elementName);
8 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "name": "react-todo-app",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "prod": "webpack --mode production"
8 | },
9 | "babel": {
10 | "presets": [
11 | "env",
12 | "react"
13 | ],
14 | "plugins": [
15 | "babel-plugin-styled-components",
16 | "babel-plugin-transform-class-properties",
17 | "babel-plugin-transform-object-rest-spread"
18 | ]
19 | },
20 | "dependencies": {
21 | "dotnetify": "^4.0.0",
22 | "dotnetify-elements": "^1.1.0",
23 | "react": "^16.9.0",
24 | "react-dom": "^16.9.0",
25 | "styled-components": "^4.3.2"
26 | },
27 | "devDependencies": {
28 | "aspnet-webpack": "~3.0.0",
29 | "babel-core": "~6.26.3",
30 | "babel-loader": "~7.1.4",
31 | "babel-plugin-styled-components": "~1.5.1",
32 | "babel-plugin-transform-class-properties": "~6.24.1",
33 | "babel-plugin-transform-object-rest-spread": "~6.26.0",
34 | "babel-preset-env": "~1.7.0",
35 | "babel-preset-react": "~6.24.1",
36 | "copy-webpack-plugin": "^5.0.4",
37 | "css-loader": "~0.28.11",
38 | "file-loader": "~1.1.11",
39 | "style-loader": "~0.21.0",
40 | "url-loader": "~1.0.1",
41 | "webpack": "~4.12.0",
42 | "webpack-bundle-analyzer": "^3.0.3",
43 | "webpack-cli": "~3.0.8",
44 | "webpack-dev-middleware": "~3.1.3",
45 | "webpack-hot-middleware": "~2.22.2"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if not exist node_modules call npm i
3 | if "%1" == "prod" goto :prod
4 | call npm run build
5 | dotnet run
6 | exit
7 |
8 | :prod
9 | call npm run prod
10 | dotnet run --launch-profile prod
11 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/server/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace ReactTodoApp
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | BuildWebHost(args).Run();
11 | }
12 |
13 | public static IWebHost BuildWebHost(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup()
16 | .Build();
17 | }
18 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/server/ViewModels/TodoList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reactive.Linq;
5 | using DotNetify;
6 | using DotNetify.Security;
7 |
8 | namespace ReactTodoApp
9 | {
10 | [Authorize]
11 | public class TodoList : MulticastVM
12 | {
13 | private static List _todos = new List();
14 |
15 | public class Todo
16 | {
17 | public int Id { get; set; }
18 | public string Text { get; set; }
19 | public bool Done { get; set; }
20 | }
21 |
22 | public List Todos => _todos;
23 |
24 | public string Todos_itemKey => nameof(Todo.Id);
25 |
26 | public int ItemsLeft => Todos.Count(x => !x.Done);
27 |
28 | public Action Add => text =>
29 | {
30 | var todo = new Todo { Id = text.GetHashCode(), Text = text };
31 | if(!Todos.Any(x => x.Id == todo.Id))
32 | {
33 | Todos.Add(todo);
34 | this.AddList(nameof(Todos), todo);
35 | Changed(nameof(ItemsLeft));
36 | }
37 | };
38 |
39 | public Action Update => update =>
40 | {
41 | var todo = Todos.Find(x => x.Id == update.Id);
42 | if(todo != null)
43 | {
44 | if(string.IsNullOrWhiteSpace(update.Text))
45 | Remove(update.Id);
46 | else
47 | {
48 | todo.Text = update.Text;
49 | todo.Done = update.Done;
50 | this.UpdateList(nameof(Todos), todo);
51 | Changed(nameof(ItemsLeft));
52 | }
53 | }
54 | };
55 |
56 | public Action Remove => id =>
57 | {
58 | var todo = Todos.Find(x => x.Id == id);
59 | if(todo != null)
60 | {
61 | Todos.Remove(todo);
62 | this.RemoveList(nameof(Todos), id);
63 | Changed(nameof(ItemsLeft));
64 | }
65 | };
66 | }
67 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const CopyPlugin = require('copy-webpack-plugin');
5 |
6 | module.exports = {
7 | mode: 'development',
8 | entry: {
9 | app: './client/index.js'
10 | },
11 | output: {
12 | filename: '[name].js',
13 | path: __dirname + '/wwwroot/dist',
14 | publicPath: '/dist/',
15 | libraryTarget: 'umd'
16 | },
17 | resolve: {
18 | modules: [ 'client', 'node_modules' ],
19 | extensions: [ '.js', '.jsx' ]
20 | },
21 | module: {
22 | rules: [
23 | { test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ },
24 | { test: /\.css$/, use: [ 'css-loader?minimize' ] },
25 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader' }
26 | ]
27 | },
28 | externals: {
29 | dotnetify: 'dotnetify',
30 | 'dotnetify-elements': 'dotNetifyElements',
31 | 'styled-components': 'styled'
32 | },
33 | devtool: 'source-map',
34 | plugins: [
35 | new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/),
36 | new CopyPlugin([
37 | { from: 'node_modules/dotnetify/dist/dotnetify-react.min.js' },
38 | { from: 'node_modules/dotnetify-elements/lib/basic-elements.bundle.js' },
39 | { from: 'node_modules/styled-components/dist/styled-components.min.js' }
40 | ])
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/ReactTodoApp/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ReactTodoApp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Shared/IdentityServer/IdentityServerClient.cs:
--------------------------------------------------------------------------------
1 | using IdentityModel;
2 | using IdentityModel.Client;
3 | using Microsoft.IdentityModel.Tokens;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Net.Http;
7 | using System.Security.Cryptography;
8 | using System.Threading.Tasks;
9 |
10 | namespace Shared
11 | {
12 | public class IdentityServerClient
13 | {
14 | public static async Task RequestClientCredentialsTokenAsync(HttpClient client, IdentityServerSettings settings)
15 | {
16 | var disco = await client.GetDiscoveryDocumentAsync(settings.Uri);
17 | return await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
18 | {
19 | Address = disco.TokenEndpoint,
20 | ClientId = IdentityServerConfig.MyClientId,
21 | ClientSecret = IdentityServerConfig.MyClientSecret,
22 | Scope = IdentityServerConfig.MyApiResource
23 | });
24 | }
25 |
26 | public static async Task IntrospectTokenAsync(HttpClient client, IdentityServerSettings settings, string token)
27 | {
28 | var disco = await client.GetDiscoveryDocumentAsync(settings.Uri);
29 | return await client.IntrospectTokenAsync(new TokenIntrospectionRequest
30 | {
31 | Address = disco.IntrospectionEndpoint,
32 | Token = token,
33 | ClientId = IdentityServerConfig.MyApiResource,
34 | ClientSecret = IdentityServerConfig.MyClientSecret
35 | });
36 | }
37 |
38 | public static async Task> GetIssuerSigningKeysAsync(HttpClient client, IdentityServerSettings settings)
39 | {
40 | var disco = await client.GetDiscoveryDocumentAsync(settings.Uri);
41 | if(disco?.KeySet == null)
42 | throw new Exception("Failed to get signing keys. Make sure the Portal is running.");
43 |
44 | var keys = new List();
45 | foreach(var webKey in disco.KeySet.Keys)
46 | {
47 | var key = new RsaSecurityKey(new RSAParameters
48 | {
49 | Exponent = Base64Url.Decode(webKey.E),
50 | Modulus = Base64Url.Decode(webKey.N)
51 | });
52 |
53 | key.KeyId = webKey.Kid;
54 | keys.Add(key);
55 | }
56 |
57 | return keys;
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Shared/IdentityServer/IdentityServerConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using IdentityServer4.Models;
3 |
4 | namespace Shared
5 | {
6 | public class IdentityServerConfig
7 | {
8 | public static readonly string MyClientId = "dotNetifyDemo";
9 | public static readonly string MyClientSecret = "secret";
10 | public static readonly string MyApiResource = "demo";
11 |
12 | public static IEnumerable GetClients()
13 | {
14 | return new List
15 | {
16 | new Client
17 | {
18 | ClientId = MyClientId,
19 | AllowedGrantTypes = GrantTypes.ClientCredentials,
20 | ClientSecrets = { new Secret(MyClientSecret.Sha256()) },
21 | AllowedScopes = { MyApiResource }
22 | }
23 | };
24 | }
25 |
26 | public static IEnumerable GetApiResources()
27 | {
28 | return new List
29 | {
30 | new ApiResource(MyApiResource, "DotNetify Demo") { ApiSecrets = { new Secret(MyClientSecret.Sha256()) } }
31 | };
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Shared/IdentityServer/IdentityServerSettings.cs:
--------------------------------------------------------------------------------
1 | namespace Shared
2 | {
3 | public class IdentityServerSettings
4 | {
5 | public static readonly string SectionName = "IdentityServer";
6 |
7 | public string Uri { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/Shared/Shared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5020/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "dev": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:5020/"
25 | },
26 | "prod": {
27 | "commandName": "Project",
28 | "launchBrowser": true,
29 | "environmentVariables": {
30 | "ASPNETCORE_ENVIRONMENT": "Production"
31 | },
32 | "applicationUrl": "http://localhost:5020/"
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/README.md:
--------------------------------------------------------------------------------
1 | ## How to run:
2 |
3 | Start the Portal first (app requires an auth server), then execute `run.bat [dev|prod]`.
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/VueTodoApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | VueTodoApp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | %(DistFiles.Identity)
41 | PreserveNewest
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://localhost:5000"
4 | }
5 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/client/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import vueCustomElement from 'vue-custom-element';
3 | import TodoList from './components/TodoList.vue';
4 |
5 | const elementName = 'vue-todo-app';
6 |
7 | Vue.use(vueCustomElement);
8 | Vue.customElement(elementName, TodoList, {
9 | shadow: false, // Don't use shadow-DOM so global css can affect the inner Vue component.
10 | destroyTimeout: 1 // Immediately destroy the Vue component when the custom element is detached.
11 | });
12 |
13 | export default document.createElement(elementName);
14 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "name": "vue-todo-app",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "prod": "webpack --mode production"
8 | },
9 | "dependencies": {
10 | "dotnetify": "^4.0.0",
11 | "dotnetify-elements": "^1.1.0",
12 | "vue": "^2.5.22",
13 | "vue-custom-element": "^3.2.9"
14 | },
15 | "devDependencies": {
16 | "aspnet-webpack": "~3.0.0",
17 | "babel-core": "~6.26.3",
18 | "babel-loader": "~7.1.4",
19 | "babel-preset-env": "~1.7.0",
20 | "copy-webpack-plugin": "^5.0.4",
21 | "css-loader": "^3.2.0",
22 | "node-sass": "~4.9.4",
23 | "sass-loader": "~7.1.0",
24 | "url-loader": "~1.0.1",
25 | "vue-loader": "^15.4.2",
26 | "vue-template-compiler": "^2.5.22",
27 | "webpack": "~4.12.0",
28 | "webpack-bundle-analyzer": "^3.0.3",
29 | "webpack-cli": "~3.0.8",
30 | "webpack-dev-middleware": "~3.1.3",
31 | "webpack-hot-middleware": "~2.22.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if not exist node_modules call npm i
3 | if "%1" == "prod" goto :prod
4 | call npm run build
5 | dotnet run
6 | exit
7 |
8 | :prod
9 | call npm run prod
10 | dotnet run --launch-profile prod
11 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/server/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace VueTodoApp
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | BuildWebHost(args).Run();
11 | }
12 |
13 | public static IWebHost BuildWebHost(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup()
16 | .Build();
17 | }
18 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/server/ViewModels/TodoList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reactive.Linq;
5 | using DotNetify;
6 | using DotNetify.Security;
7 |
8 | namespace VueTodoApp
9 | {
10 | [Authorize]
11 | public class TodoList : MulticastVM
12 | {
13 | private static List _todos = new List();
14 |
15 | public class Todo
16 | {
17 | public int Id { get; set; }
18 | public string Text { get; set; }
19 | public bool Done { get; set; }
20 | }
21 |
22 | public List Todos => _todos;
23 |
24 | public string Todos_itemKey => nameof(Todo.Id);
25 |
26 | public int ItemsLeft => Todos.Count(x => !x.Done);
27 |
28 | public Action Add => text =>
29 | {
30 | var todo = new Todo { Id = text.GetHashCode(), Text = text };
31 | if (!Todos.Any(x => x.Id == todo.Id))
32 | {
33 | Todos.Add(todo);
34 | this.AddList(nameof(Todos), todo);
35 | Changed(nameof(ItemsLeft));
36 | }
37 | };
38 |
39 | public Action Update => update =>
40 | {
41 | var todo = Todos.Find(x => x.Id == update.Id);
42 | if (todo != null)
43 | {
44 | if (string.IsNullOrWhiteSpace(update.Text))
45 | Remove(update.Id);
46 | else
47 | {
48 | todo.Text = update.Text;
49 | todo.Done = update.Done;
50 | this.UpdateList(nameof(Todos), todo);
51 | Changed(nameof(ItemsLeft));
52 | }
53 | }
54 | };
55 |
56 | public Action Remove => id =>
57 | {
58 | var todo = Todos.Find(x => x.Id == id);
59 | if (todo != null)
60 | {
61 | Todos.Remove(todo);
62 | this.RemoveList(nameof(Todos), id);
63 | Changed(nameof(ItemsLeft));
64 | }
65 | };
66 | }
67 | }
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const CopyPlugin = require('copy-webpack-plugin');
5 | const { VueLoaderPlugin } = require('vue-loader');
6 |
7 | module.exports = {
8 | mode: 'development',
9 | entry: {
10 | app: './client/index.js'
11 | },
12 | output: {
13 | filename: '[name].js',
14 | path: __dirname + '/wwwroot/dist',
15 | publicPath: '/dist/',
16 | libraryTarget: 'umd'
17 | },
18 | resolve: {
19 | modules: [ 'client', 'node_modules' ],
20 | alias: { vue$: 'vue/dist/vue.esm.js' }
21 | },
22 | module: {
23 | rules: [
24 | { test: /\.vue$/, use: 'vue-loader' },
25 | { test: /\.js?$/, loader: 'babel-loader', query: { presets: [ 'env' ] } },
26 | { test: /\.scss$/, use: [ 'vue-style-loader', 'css-loader', 'sass-loader' ] },
27 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader' }
28 | ]
29 | },
30 | externals: {
31 | 'dotnetify-elements': 'dotNetifyElements'
32 | },
33 | devtool: 'source-map',
34 | plugins: [
35 | new VueLoaderPlugin(),
36 | new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/),
37 | new CopyPlugin([
38 | { from: 'node_modules/dotnetify/dist/dotnetify-vue.min.js' },
39 | { from: 'node_modules/dotnetify-elements/lib/basic-web-components.bundle.js' }
40 | ])
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/VueTodoApp/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | VueTodoApp
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "3.1.201"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/Portal/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
2 | LABEL stage=build
3 | WORKDIR /src
4 | COPY ./__tmp__ .
5 |
6 | # Install node.js
7 | RUN apt-get update -yq \
8 | && apt-get install curl gnupg -yq \
9 | && curl -sL https://deb.nodesource.com/setup_12.x | bash \
10 | && apt-get install nodejs -yq
11 |
12 | RUN dotnet publish ./Portal/Portal.csproj -c Release -o /app
13 |
14 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
15 | WORKDIR /app
16 | COPY --from=build /app .
17 | ARG aspnetenv=Production
18 |
19 | ENV ASPNETCORE_ENVIRONMENT ${aspnetenv}
20 | CMD ASPNETCORE_URLS=http://*:$PORT dotnet Portal.dll
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/Portal/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://dotnetify-mfe.herokuapp.com"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/Portal/build.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | set env=Production
3 | if "%1"=="" goto :next
4 | set env=%1
5 | :next
6 |
7 | echo --- Copy source code
8 | xcopy ..\..\Portal\*.* .\__tmp__\Portal\ /q /s /e /d /y /exclude:excludedfiles.txt
9 | xcopy ..\..\Shared\*.* .\__tmp__\Shared\ /q /s /e /d /y /exclude:excludedfiles.txt
10 |
11 | echo --- Remove any existing image
12 | docker rmi portal -f
13 |
14 | echo --- Build a new image
15 | docker build -t portal -f ./Dockerfile . --build-arg aspnetenv=%env%
16 |
17 | echo --- Remove build images
18 | docker image prune -f --filter label=stage=build
19 | rd __tmp__ /q /s
20 |
21 | echo --- Run a container on port 5000
22 | docker run -it --rm -p:5000:80 --name portal_5000 portal
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/Portal/deploy.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | echo --- Copy source code
4 | xcopy ..\..\Portal\*.* .\__tmp__\Portal\ /q /s /e /d /y /exclude:excludedfiles.txt
5 | xcopy ..\..\Shared\*.* .\__tmp__\Shared\ /q /s /e /d /y /exclude:excludedfiles.txt
6 |
7 | xcopy appsettings.json .\__tmp__\Portal\ /q /y
8 |
9 | call heroku container:push web -a dotnetify-portal
10 | call heroku container:release web -a dotnetify-portal
11 |
12 | rd __tmp__ /q /s
13 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/Portal/excludedfiles.txt:
--------------------------------------------------------------------------------
1 | .vssscc
2 | .vspscc
3 | .log
4 | .user
5 | \obj\
6 | \bin\
7 | \Properties\
8 | \node_modules\
9 | \coverage\
10 | \wwwroot\dist\
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/ReactDashboardApp/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
2 | LABEL stage=build
3 | WORKDIR /src
4 | COPY ./__tmp__ .
5 |
6 | # Install node.js
7 | RUN apt-get update -yq \
8 | && apt-get install curl gnupg -yq \
9 | && curl -sL https://deb.nodesource.com/setup_12.x | bash \
10 | && apt-get install nodejs -yq
11 |
12 | RUN dotnet publish ./ReactDashboardApp/ReactDashboardApp.csproj -c Release -o /app
13 |
14 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
15 | WORKDIR /app
16 | COPY --from=build /app .
17 | ARG aspnetenv=Production
18 |
19 | ENV ASPNETCORE_ENVIRONMENT ${aspnetenv}
20 | CMD ASPNETCORE_URLS=http://*:$PORT dotnet ReactDashboardApp.dll
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/ReactDashboardApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "IdentityServer": {
3 | "Uri": "http://dotnetify-mfe.herokuapp.com"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/ReactDashboardApp/build.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | set env=Production
3 | if "%1"=="" goto :next
4 | set env=%1
5 | :next
6 |
7 | echo --- Copy source code
8 | xcopy ..\..\ReactDashboardApp\*.* .\__tmp__\ReactDashboardApp\ /q /s /e /d /y /exclude:excludedfiles.txt
9 | xcopy ..\..\Shared\*.* .\__tmp__\Shared\ /q /s /e /d /y /exclude:excludedfiles.txt
10 |
11 | echo --- Remove any existing image
12 | docker rmi dashboard -f
13 |
14 | echo --- Build a new image
15 | docker build -t portal -f ./Dockerfile . --build-arg aspnetenv=%env%
16 |
17 | echo --- Remove build images
18 | docker image prune -f --filter label=stage=build
19 | rd __tmp__ /q /s
20 |
21 | echo --- Run a container on port 5060
22 | docker run -it --rm -p:5060:80 --name dashboard_5060 dashboard
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/ReactDashboardApp/deploy.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | echo --- Copy source code
4 | xcopy ..\..\ReactDashboardApp\*.* .\__tmp__\ReactDashboardApp\ /q /s /e /d /y /exclude:excludedfiles.txt
5 | xcopy ..\..\Shared\*.* .\__tmp__\Shared\ /q /s /e /d /y /exclude:excludedfiles.txt
6 |
7 | xcopy appsettings.json .\__tmp__\ReactDashboardApp\ /q /y
8 |
9 | call heroku container:push web -a dotnetify-dashboard
10 | call heroku container:release web -a dotnetify-dashboard
11 |
12 | rd __tmp__ /q /s
13 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/ReactDashboardApp/excludedfiles.txt:
--------------------------------------------------------------------------------
1 | .vssscc
2 | .vspscc
3 | .log
4 | .user
5 | \obj\
6 | \bin\
7 | \Properties\
8 | \node_modules\
9 | \coverage\
10 | \wwwroot\dist\
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:alpine
2 | COPY ./nginx.conf /etc/nginx
3 | CMD sed -i -e 's/$PORT/'"$PORT"'/g' /etc/nginx/nginx.conf && nginx -g 'daemon off;'
--------------------------------------------------------------------------------
/MicrofrontendTemplate/heroku-deploy/nginx/deploy.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | call heroku container:push web -a dotnetify-mfe
4 | call heroku container:release web -a dotnetify-mfe
5 |
6 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/.gitignore:
--------------------------------------------------------------------------------
1 | logs
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/conf/fastcgi.conf:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
3 | fastcgi_param QUERY_STRING $query_string;
4 | fastcgi_param REQUEST_METHOD $request_method;
5 | fastcgi_param CONTENT_TYPE $content_type;
6 | fastcgi_param CONTENT_LENGTH $content_length;
7 |
8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
9 | fastcgi_param REQUEST_URI $request_uri;
10 | fastcgi_param DOCUMENT_URI $document_uri;
11 | fastcgi_param DOCUMENT_ROOT $document_root;
12 | fastcgi_param SERVER_PROTOCOL $server_protocol;
13 | fastcgi_param REQUEST_SCHEME $scheme;
14 | fastcgi_param HTTPS $https if_not_empty;
15 |
16 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
17 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
18 |
19 | fastcgi_param REMOTE_ADDR $remote_addr;
20 | fastcgi_param REMOTE_PORT $remote_port;
21 | fastcgi_param SERVER_ADDR $server_addr;
22 | fastcgi_param SERVER_PORT $server_port;
23 | fastcgi_param SERVER_NAME $server_name;
24 |
25 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
26 | fastcgi_param REDIRECT_STATUS 200;
27 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/conf/fastcgi_params:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param QUERY_STRING $query_string;
3 | fastcgi_param REQUEST_METHOD $request_method;
4 | fastcgi_param CONTENT_TYPE $content_type;
5 | fastcgi_param CONTENT_LENGTH $content_length;
6 |
7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
8 | fastcgi_param REQUEST_URI $request_uri;
9 | fastcgi_param DOCUMENT_URI $document_uri;
10 | fastcgi_param DOCUMENT_ROOT $document_root;
11 | fastcgi_param SERVER_PROTOCOL $server_protocol;
12 | fastcgi_param REQUEST_SCHEME $scheme;
13 | fastcgi_param HTTPS $https if_not_empty;
14 |
15 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
16 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
17 |
18 | fastcgi_param REMOTE_ADDR $remote_addr;
19 | fastcgi_param REMOTE_PORT $remote_port;
20 | fastcgi_param SERVER_ADDR $server_addr;
21 | fastcgi_param SERVER_PORT $server_port;
22 | fastcgi_param SERVER_NAME $server_name;
23 |
24 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
25 | fastcgi_param REDIRECT_STATUS 200;
26 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/conf/nginx.conf:
--------------------------------------------------------------------------------
1 |
2 | worker_processes 1;
3 |
4 | events {
5 | worker_connections 1024;
6 | }
7 |
8 | http {
9 | log_format main '$remote_addr - $remote_user [$time_local] "$request" '
10 | '$status $body_bytes_sent "$http_referer" '
11 | '"$http_user_agent" "$http_x_forwarded_for"';
12 |
13 | server {
14 | listen 8080;
15 | server_name localhost;
16 |
17 | location / {
18 | proxy_pass http://localhost:5000;
19 | proxy_http_version 1.1;
20 | proxy_set_header Upgrade $http_upgrade;
21 | proxy_set_header Connection $http_connection;
22 | proxy_set_header Host $host;
23 | proxy_cache_bypass $http_upgrade;
24 | }
25 |
26 | location /app1 {
27 | rewrite ^/app1/(.*) /$1 break;
28 | proxy_pass http://localhost:5060;
29 | proxy_http_version 1.1;
30 | proxy_set_header Upgrade $http_upgrade;
31 | proxy_set_header Connection $http_connection;
32 | proxy_set_header Host $host;
33 | proxy_cache_bypass $http_upgrade;
34 | }
35 |
36 | location /app2 {
37 | rewrite ^/app2/(.*) /$1 break;
38 | proxy_pass http://localhost:5070;
39 | proxy_http_version 1.1;
40 | proxy_set_header Upgrade $http_upgrade;
41 | proxy_set_header Connection $http_connection;
42 | proxy_set_header Host $host;
43 | proxy_cache_bypass $http_upgrade;
44 | }
45 |
46 | location /app3 {
47 | rewrite ^/app3/(.*) /$1 break;
48 | proxy_pass http://localhost:5010;
49 | proxy_http_version 1.1;
50 | proxy_set_header Upgrade $http_upgrade;
51 | proxy_set_header Connection $http_connection;
52 | proxy_set_header Host $host;
53 | proxy_cache_bypass $http_upgrade;
54 | }
55 |
56 | location /app4 {
57 | rewrite ^/app4/(.*) /$1 break;
58 | proxy_pass http://localhost:5020;
59 | proxy_http_version 1.1;
60 | proxy_set_header Upgrade $http_upgrade;
61 | proxy_set_header Connection $http_connection;
62 | proxy_set_header Host $host;
63 | proxy_cache_bypass $http_upgrade;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/conf/scgi_params:
--------------------------------------------------------------------------------
1 |
2 | scgi_param REQUEST_METHOD $request_method;
3 | scgi_param REQUEST_URI $request_uri;
4 | scgi_param QUERY_STRING $query_string;
5 | scgi_param CONTENT_TYPE $content_type;
6 |
7 | scgi_param DOCUMENT_URI $document_uri;
8 | scgi_param DOCUMENT_ROOT $document_root;
9 | scgi_param SCGI 1;
10 | scgi_param SERVER_PROTOCOL $server_protocol;
11 | scgi_param REQUEST_SCHEME $scheme;
12 | scgi_param HTTPS $https if_not_empty;
13 |
14 | scgi_param REMOTE_ADDR $remote_addr;
15 | scgi_param REMOTE_PORT $remote_port;
16 | scgi_param SERVER_PORT $server_port;
17 | scgi_param SERVER_NAME $server_name;
18 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/conf/uwsgi_params:
--------------------------------------------------------------------------------
1 |
2 | uwsgi_param QUERY_STRING $query_string;
3 | uwsgi_param REQUEST_METHOD $request_method;
4 | uwsgi_param CONTENT_TYPE $content_type;
5 | uwsgi_param CONTENT_LENGTH $content_length;
6 |
7 | uwsgi_param REQUEST_URI $request_uri;
8 | uwsgi_param PATH_INFO $document_uri;
9 | uwsgi_param DOCUMENT_ROOT $document_root;
10 | uwsgi_param SERVER_PROTOCOL $server_protocol;
11 | uwsgi_param REQUEST_SCHEME $scheme;
12 | uwsgi_param HTTPS $https if_not_empty;
13 |
14 | uwsgi_param REMOTE_ADDR $remote_addr;
15 | uwsgi_param REMOTE_PORT $remote_port;
16 | uwsgi_param SERVER_PORT $server_port;
17 | uwsgi_param SERVER_NAME $server_name;
18 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/contrib/README:
--------------------------------------------------------------------------------
1 |
2 | geo2nginx.pl by Andrei Nigmatulin
3 |
4 | The perl script to convert CSV geoip database ( free download
5 | at http://www.maxmind.com/app/geoip_country ) to format, suitable
6 | for use by the ngx_http_geo_module.
7 |
8 |
9 | unicode2nginx by Maxim Dounin
10 |
11 | The perl script to convert unicode mappings ( available
12 | at http://www.unicode.org/Public/MAPPINGS/ ) to the nginx
13 | configuration file format.
14 | Two generated full maps for windows-1251 and koi8-r.
15 |
16 |
17 | vim by Evan Miller
18 |
19 | Syntax highlighting of nginx configuration for vim, to be
20 | placed into ~/.vim/.
21 |
22 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/contrib/geo2nginx.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 |
3 | # (c) Andrei Nigmatulin, 2005
4 | #
5 | # this script provided "as is", without any warranties. use it at your own risk.
6 | #
7 | # special thanx to Andrew Sitnikov for perl port
8 | #
9 | # this script converts CSV geoip database (free download at http://www.maxmind.com/app/geoip_country)
10 | # to format, suitable for use with nginx_http_geo module (http://sysoev.ru/nginx)
11 | #
12 | # for example, line with ip range
13 | #
14 | # "62.16.68.0","62.16.127.255","1041253376","1041268735","RU","Russian Federation"
15 | #
16 | # will be converted to four subnetworks:
17 | #
18 | # 62.16.68.0/22 RU;
19 | # 62.16.72.0/21 RU;
20 | # 62.16.80.0/20 RU;
21 | # 62.16.96.0/19 RU;
22 |
23 |
24 | use warnings;
25 | use strict;
26 |
27 | while( ){
28 | if (/"[^"]+","[^"]+","([^"]+)","([^"]+)","([^"]+)"/){
29 | print_subnets($1, $2, $3);
30 | }
31 | }
32 |
33 | sub print_subnets {
34 | my ($a1, $a2, $c) = @_;
35 | my $l;
36 | while ($a1 <= $a2) {
37 | for ($l = 0; ($a1 & (1 << $l)) == 0 && ($a1 + ((1 << ($l + 1)) - 1)) <= $a2; $l++){};
38 | print long2ip($a1) . "/" . (32 - $l) . " " . $c . ";\n";
39 | $a1 += (1 << $l);
40 | }
41 | }
42 |
43 | sub long2ip {
44 | my $ip = shift;
45 |
46 | my $str = 0;
47 |
48 | $str = ($ip & 255);
49 |
50 | $ip >>= 8;
51 | $str = ($ip & 255).".$str";
52 |
53 | $ip >>= 8;
54 | $str = ($ip & 255).".$str";
55 |
56 | $ip >>= 8;
57 | $str = ($ip & 255).".$str";
58 | }
59 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/contrib/unicode2nginx/unicode-to-nginx.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w
2 |
3 | # Convert unicode mappings to nginx configuration file format.
4 |
5 | # You may find useful mappings in various places, including
6 | # unicode.org official site:
7 | #
8 | # http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT
9 | # http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT
10 |
11 | # Needs perl 5.6 or later.
12 |
13 | # Written by Maxim Dounin, mdounin@mdounin.ru
14 |
15 | ###############################################################################
16 |
17 | require 5.006;
18 |
19 | while (<>) {
20 | # Skip comments and empty lines
21 |
22 | next if /^#/;
23 | next if /^\s*$/;
24 | chomp;
25 |
26 | # Convert mappings
27 |
28 | if (/^\s*0x(..)\s*0x(....)\s*(#.*)/) {
29 | # Mapping "#"
30 | my $cs_code = $1;
31 | my $un_code = $2;
32 | my $un_name = $3;
33 |
34 | # Produce UTF-8 sequence from character code;
35 |
36 | my $un_utf8 = join('',
37 | map { sprintf("%02X", $_) }
38 | unpack("U0C*", pack("U", hex($un_code)))
39 | );
40 |
41 | print " $cs_code $un_utf8 ; $un_name\n";
42 |
43 | } else {
44 | warn "Unrecognized line: '$_'";
45 | }
46 | }
47 |
48 | ###############################################################################
49 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/contrib/vim/ftdetect/nginx.vim:
--------------------------------------------------------------------------------
1 | au BufRead,BufNewFile *.nginx set ft=nginx
2 | au BufRead,BufNewFile */etc/nginx/* set ft=nginx
3 | au BufRead,BufNewFile */usr/local/nginx/conf/* set ft=nginx
4 | au BufRead,BufNewFile nginx.conf set ft=nginx
5 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/contrib/vim/ftplugin/nginx.vim:
--------------------------------------------------------------------------------
1 | setlocal commentstring=#\ %s
2 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/contrib/vim/indent/nginx.vim:
--------------------------------------------------------------------------------
1 | if exists("b:did_indent")
2 | finish
3 | endif
4 | let b:did_indent = 1
5 |
6 | setlocal indentexpr=
7 |
8 | " cindent actually works for nginx' simple file structure
9 | setlocal cindent
10 | " Just make sure that the comments are not reset as defs would be.
11 | setlocal cinkeys-=0#
12 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/docs/LICENSE:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2002-2019 Igor Sysoev
3 | * Copyright (C) 2011-2019 Nginx, Inc.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | * 1. Redistributions of source code must retain the above copyright
10 | * notice, this list of conditions and the following disclaimer.
11 | * 2. Redistributions in binary form must reproduce the above copyright
12 | * notice, this list of conditions and the following disclaimer in the
13 | * documentation and/or other materials provided with the distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 | * SUCH DAMAGE.
26 | */
27 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/docs/README:
--------------------------------------------------------------------------------
1 |
2 | Documentation is available at http://nginx.org
3 |
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/docs/zlib.LICENSE:
--------------------------------------------------------------------------------
1 | (C) 1995-2017 Jean-loup Gailly and Mark Adler
2 |
3 | This software is provided 'as-is', without any express or implied
4 | warranty. In no event will the authors be held liable for any damages
5 | arising from the use of this software.
6 |
7 | Permission is granted to anyone to use this software for any purpose,
8 | including commercial applications, and to alter it and redistribute it
9 | freely, subject to the following restrictions:
10 |
11 | 1. The origin of this software must not be misrepresented; you must not
12 | claim that you wrote the original software. If you use this software
13 | in a product, an acknowledgment in the product documentation would be
14 | appreciated but is not required.
15 | 2. Altered source versions must be plainly marked as such, and must not be
16 | misrepresented as being the original software.
17 | 3. This notice may not be removed or altered from any source distribution.
18 |
19 | Jean-loup Gailly Mark Adler
20 | jloup@gzip.org madler@alumni.caltech.edu
21 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/html/50x.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Error
5 |
12 |
13 |
14 | An error occurred.
15 | Sorry, the page you are looking for is currently unavailable.
16 | Please try again later.
17 | If you are the system administrator of this resource then you should check
18 | the error log for details.
19 | Faithfully yours, nginx.
20 |
21 |
22 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Welcome to nginx!
5 |
12 |
13 |
14 | Welcome to nginx!
15 | If you see this page, the nginx web server is successfully installed and
16 | working. Further configuration is required.
17 |
18 | For online documentation and support please refer to
19 | nginx.org.
20 | Commercial support is available at
21 | nginx.com.
22 |
23 | Thank you for using nginx.
24 |
25 |
26 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/nginx.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsuryd/dotNetify-react-template/c1f80d7cdb751069eaa5f7ed7e74680997be1eb9/MicrofrontendTemplate/nginx/nginx.exe
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/run.bat:
--------------------------------------------------------------------------------
1 | if not exist logs mkdir logs
2 | if not exist temp mkdir temp
3 | nginx
4 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/nginx/stop.bat:
--------------------------------------------------------------------------------
1 | nginx -s stop
2 |
--------------------------------------------------------------------------------
/MicrofrontendTemplate/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | if "%1" == "apps" goto :apps
3 |
4 | cd nginx
5 | start cmd /k run.bat
6 | cd ..
7 |
8 | cd Portal
9 | start cmd /k run prod
10 | cd ..
11 | @echo Wait until the portal app started, then type "run apps" to start the other apps.
12 | @echo Open the website at http://localhost:8080.
13 | pause
14 | goto :end
15 |
16 | :apps
17 | cd ReactDashboardApp
18 | start cmd /k run prod
19 |
20 | cd ../ReactFormApp
21 | start cmd /k run prod
22 |
23 | cd ../ReactTodoApp
24 | start cmd /k run prod
25 |
26 | cd ../VueTodoApp
27 | start cmd /k run prod
28 |
29 | cd ..
30 |
31 | :end
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## DotNetify React Templates
4 |
5 | DotNetify is a [free, open source project](https://github.com/dsuryd/dotNetify) that lets you create real-time, reactive, cross-platform apps with React, React Native, or Knockout front-end on C# .NET back-end via WebSocket.
6 |
7 | ### React Template
8 |
9 | This is a full React SPA template for ASP.NET 6 featuring:
10 | - Reactive, real-time dashboard page.
11 | - Edit form + CRUD table pages.
12 | - Login page with JWT bearer token authentication.
13 | - UI components from [Material-UI](https://material-ui.com/).
14 | - Routing with deep links.
15 | - [OpenID Connect/OAuth2](https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server) authentication server.
16 |
17 | ### Micro-Frontend Template
18 |
19 | Reference implementation of micro-frontend [as described here](https://dotnetify.net/core/mfe).
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ReactTemplate/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## DotNetify React Template
4 |
5 | DotNetify is a [free, open source project](https://github.com/dsuryd/dotNetify) that lets you create real-time, reactive, cross-platform apps with React, React Native, or Knockout front-end on C# .NET back-end via WebSocket.
6 |
7 | This is a full React SPA template for ASP.NET Core featuring:
8 |
9 | - Reactive, real-time dashboard page.
10 | - Edit form + CRUD table pages.
11 | - Login page with JWT bearer token authentication.
12 | - UI components from [Material-UI](https://material-ui.com/).
13 | - Routing with deep links.
14 | - Webpack hot module replacement + [dotnet watch](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch).
15 | - [OpenID Connect/OAuth2](https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server) authentication server.
16 |
17 | ### How to install from NuGet
18 |
19 | ```
20 | dotnet new -i DotNetify.React.Template
21 |
22 | dotnet new dotnetify -o MyApp
23 | cd MyApp
24 | npm i
25 | dotnet watch
26 | ```
27 |
28 | Open http://localhost:5000.
29 |
30 | 
31 |
32 | ### How to make it work with IE 11
33 |
34 | Add the following scripts in `index.html`:
35 |
36 | ```
37 |
38 |
39 |
40 | ```
41 |
42 | ### Documentation
43 |
44 | Documentation and live demo are available at [http://dotnetify.net/react](http://dotnetify.net/react).
45 |
46 | ### Credits
47 |
48 | The UI layout was adapted from the [work by @rafaelhz](https://github.com/rafaelhz/react-material-admin-template).
49 |
--------------------------------------------------------------------------------
/ReactTemplate/content/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /bin
6 | /obj
7 |
8 | #Visual Studio
9 | .vs
10 | .vscode
11 | *.csproj.user
12 | /Properties/launchSettings.json
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | npm-debug.log*
18 | yarn-debug.log*
19 | yarn-error.log*
20 | wwwroot/dist
21 |
22 | #Typescript
23 | /typings
--------------------------------------------------------------------------------
/ReactTemplate/content/.template.config/template.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsuryd/dotNetify-react-template/c1f80d7cdb751069eaa5f7ed7e74680997be1eb9/ReactTemplate/content/.template.config/template.json
--------------------------------------------------------------------------------
/ReactTemplate/content/client/app.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './routes';
4 | import 'flexboxgrid/css/flexboxgrid.css';
5 | import './styles/app.css';
6 |
7 | ReactDOM.render(, document.getElementById('App'));
8 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/auth.ts:
--------------------------------------------------------------------------------
1 | class Auth {
2 | url = '/token';
3 |
4 | signIn(username: string, password: string): Promise {
5 | return fetch(this.url, {
6 | method: 'post',
7 | mode: 'no-cors',
8 | body: 'username=' + username + '&password=' + password + '&grant_type=password&client_id=dotnetifydemo',
9 | headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
10 | })
11 | .then(response => {
12 | if (!response.ok) throw new Error(`${response.status}`);
13 | return response.json();
14 | })
15 | .then(token => {
16 | window.sessionStorage.setItem('access_token', token.access_token);
17 | });
18 | }
19 |
20 | signOut() {
21 | window.sessionStorage.removeItem('access_token');
22 | window.location.href = '/';
23 | }
24 |
25 | getAccessToken(): string {
26 | return window.sessionStorage.getItem('access_token');
27 | }
28 |
29 | hasAccessToken(): boolean {
30 | return this.getAccessToken() != null;
31 | }
32 | }
33 |
34 | export default new Auth();
35 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/components/BasePage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Paper from '@material-ui/core/Paper';
3 | import Divider from '@material-ui/core/Divider';
4 | import globalStyles from '../styles/styles';
5 |
6 | export interface IBasePageProps {
7 | title: string;
8 | navigation: string;
9 | children: React.ReactNode;
10 | }
11 |
12 | export default function BasePage({ title, navigation, children }: IBasePageProps) {
13 | return (
14 |
15 |
{navigation}
16 |
17 | {title}
18 |
19 | {children}
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import AppBar from '@material-ui/core/AppBar';
4 | import Toolbar from '@material-ui/core/Toolbar';
5 | import IconButton from '@material-ui/core/IconButton';
6 | import Menu from '@material-ui/core/Menu';
7 | import MenuItem from '@material-ui/core/MenuItem';
8 | import MoreVertIcon from '@material-ui/icons/MoreVert';
9 | import MenuIcon from '@material-ui/icons/Menu';
10 | import blue from '@material-ui/core/colors/blue';
11 | import auth from '../auth';
12 |
13 | const useStyles = makeStyles({
14 | root: {
15 | flexGrow: 1
16 | },
17 | appBar: {
18 | backgroundColor: blue[600],
19 | overflow: 'hidden',
20 | position: 'fixed',
21 | top: 0,
22 | maxHeight: 56
23 | },
24 | menuButton: {
25 | marginLeft: -24
26 | },
27 | morebutton: {
28 | color: 'white'
29 | },
30 | title: {
31 | flexGrow: 1
32 | }
33 | });
34 |
35 | export interface IHeaderProps {
36 | styles: React.CSSProperties;
37 | onSidebarToggle: (event: React.MouseEvent) => void;
38 | }
39 |
40 | export default function Header({ styles, onSidebarToggle }: IHeaderProps) {
41 | const [anchorEl, setAnchorEl] = React.useState(null);
42 | const classes = useStyles({});
43 |
44 | const handleIconClick = (event: React.MouseEvent) => setAnchorEl(event.currentTarget);
45 | const handleMenuClose = () => setAnchorEl(null);
46 | const handleMenuClick = () => auth.signOut();
47 |
48 | return (
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
63 |
64 |
65 |
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/components/dashboard/InfoBox.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { makeStyles } from "@material-ui/core/styles";
3 | import Card from "@material-ui/core/Card";
4 | import grey from "@material-ui/core/colors/grey";
5 |
6 | const useStyles = makeStyles({
7 | content: {
8 | padding: "5px 10px",
9 | marginLeft: 90,
10 | height: 80
11 | },
12 | number: {
13 | display: "block",
14 | fontWeight: "bold",
15 | fontSize: 18,
16 | paddingTop: 10,
17 | color: grey[800]
18 | },
19 | text: {
20 | fontSize: 18,
21 | fontWeight: "lighter",
22 | color: grey[600]
23 | },
24 | iconBox: {
25 | float: "left",
26 | height: 90,
27 | width: 90,
28 | textAlign: "center",
29 | color: "white",
30 | backgroundColor: (props: any) => props.color
31 | },
32 | icon: {
33 | height: 48,
34 | width: 48,
35 | marginTop: 20,
36 | maxWidth: "100%"
37 | }
38 | });
39 |
40 | export interface IInfoBoxProps {
41 | icon: React.ComponentType;
42 | color: string;
43 | title: string;
44 | value: any;
45 | }
46 |
47 | export default function InfoBox({ icon, color, title, value }: IInfoBoxProps) {
48 | const classes = useStyles({ color });
49 | const Icon: any = icon;
50 |
51 | return (
52 |
53 |
54 |
55 |
56 |
57 |
58 | {title}
59 | {value}
60 |
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/components/dashboard/ServerUsage.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Chart as ChartJS, CategoryScale, LinearScale, ArcElement, BarElement, Title, Tooltip, Legend } from "chart.js";
3 | import { Bar } from "react-chartjs-2";
4 | import { makeStyles } from "@material-ui/core/styles";
5 | import Card from "@material-ui/core/Card";
6 | import pink from "@material-ui/core/colors/pink";
7 | import globalStyles from "../../styles/styles";
8 |
9 | ChartJS.register(CategoryScale, LinearScale, ArcElement, BarElement, Title, Tooltip, Legend);
10 |
11 | const useStyles = makeStyles({
12 | card: {
13 | backgroundColor: pink[600],
14 | height: 150
15 | },
16 | header: {
17 | color: "white",
18 | backgroundColor: pink[500],
19 | padding: 10
20 | },
21 | body: {
22 | marginLeft: "auto",
23 | marginRight: "auto",
24 | width: "95%",
25 | height: 85
26 | }
27 | });
28 |
29 | const chartOptions = {
30 | plugins: {
31 | legend: { display: false },
32 | tooltips: { enabled: false }
33 | },
34 | scales: {
35 | x: { ticks: { color: "white" }, grid: { display: false } },
36 | y: { ticks: { display: false } }
37 | },
38 | layout: { padding: { bottom: 5 } },
39 | maintainAspectRatio: false
40 | };
41 |
42 | export interface IServerUsageProps {
43 | labels: string[];
44 | data: number[];
45 | }
46 |
47 | export default function ServerUsage(props: IServerUsageProps) {
48 | const classes = useStyles({});
49 | const data = {
50 | labels: props.labels,
51 | datasets: [
52 | {
53 | data: props.data,
54 | backgroundColor: pink[400],
55 | borderColor: pink[500]
56 | }
57 | ]
58 | };
59 |
60 | const titleStyle = { ...globalStyles.title };
61 |
62 | return (
63 |
64 |
65 | Server Usage
66 |
67 |
68 |
69 |
70 |
71 | );
72 | }
73 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/components/dashboard/Traffic.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from "chart.js";
3 | import { Line } from "react-chartjs-2";
4 | import { makeStyles } from "@material-ui/core/styles";
5 | import Card from "@material-ui/core/Card";
6 | import purple from "@material-ui/core/colors/purple";
7 |
8 | ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
9 |
10 | const useStyles = makeStyles({
11 | card: {
12 | backgroundColor: purple[500],
13 | height: 150
14 | },
15 | header: {
16 | fontSize: 24,
17 | color: "white",
18 | backgroundColor: purple[600],
19 | padding: 10
20 | },
21 | body: {
22 | height: 95,
23 | padding: "5px 15px 0 15px"
24 | }
25 | });
26 |
27 | const chartOptions = {
28 | plugins: {
29 | legend: { display: false }
30 | },
31 | scales: { xAxis: { ticks: { display: false } }, yAxis: { ticks: { display: false } } },
32 | layout: { padding: { left: 5, right: 5, top: 5, bottom: 5 } },
33 | maintainAspectRatio: false
34 | };
35 |
36 | export interface ITrafficProps {
37 | data: number[];
38 | }
39 |
40 | export default function Traffic(props: ITrafficProps) {
41 | const classes = useStyles({});
42 | const data = {
43 | labels: props.data.map(_ => ""),
44 | datasets: [
45 | {
46 | data: props.data,
47 | fill: false,
48 | backgroundColor: "white",
49 | borderColor: "#8884d8",
50 | borderWidth: 2,
51 | pointBorderWidth: 2
52 | }
53 | ]
54 | };
55 |
56 | return (
57 |
58 | Traffic
59 |
60 |
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/components/table/Pagination.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import Paper from '@material-ui/core/Paper';
4 | import Button from '@material-ui/core/Button';
5 |
6 | const useStyles = makeStyles({
7 | paper: {
8 | display: 'inline',
9 | padding: '.5em 0',
10 | borderRadius: 0
11 | },
12 | button: { minWidth: '2.5em' }
13 | });
14 |
15 | export interface IPaginationProps {
16 | pages: number[];
17 | select: number;
18 | style: React.CSSProperties;
19 | onSelect: (page: any) => void;
20 | }
21 |
22 | export default function Pagination(props: IPaginationProps) {
23 | const classes = useStyles({});
24 | const pageButtons = props.pages.map(page => (
25 |
26 |
29 |
30 | ));
31 |
32 | return {pageButtons}
;
33 | }
34 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsuryd/dotNetify-react-template/c1f80d7cdb751069eaa5f7ed7e74680997be1eb9/ReactTemplate/content/client/favicon.ico
--------------------------------------------------------------------------------
/ReactTemplate/content/client/images/material_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsuryd/dotNetify-react-template/c1f80d7cdb751069eaa5f7ed7e74680997be1eb9/ReactTemplate/content/client/images/material_bg.png
--------------------------------------------------------------------------------
/ReactTemplate/content/client/routes.tsx:
--------------------------------------------------------------------------------
1 | import App from './views/App';
2 | import Dashboard from './views/Dashboard';
3 | import FormPage from './views/FormPage';
4 | import TablePage from './views/TablePage';
5 |
6 | // Import all the routeable views into the global window variable.
7 | Object.assign(window, {
8 | Dashboard,
9 | FormPage,
10 | TablePage
11 | });
12 |
13 | export default App;
14 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/styles/app.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: 'Roboto', sans-serif;
3 | -webkit-font-smoothing: antialiased;
4 | }
5 |
6 | body,
7 | h1,
8 | h2,
9 | h3,
10 | h4,
11 | h5,
12 | h6 {
13 | font-size: 15px;
14 | margin: 0;
15 | line-height: 24px;
16 | }
17 |
18 | body {
19 | margin: 0;
20 | background-color: #efefef;
21 | }
22 |
23 | body,
24 | html,
25 | #app,
26 | #app > div {
27 | height: 100%;
28 | }
29 |
30 | a {
31 | color: 'white';
32 | text-decoration: none;
33 | }
34 |
35 | a:hover {
36 | text-decoration: none;
37 | }
38 |
39 | .m-b-15 {
40 | margin-bottom: 15px;
41 | }
42 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/styles/styles.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const styles: { [name: string]: React.CSSProperties } = {
4 | navigation: {
5 | fontSize: 15,
6 | fontWeight: 'lighter',
7 | color: '#777',
8 | paddingBottom: 15,
9 | display: 'block'
10 | },
11 | title: {
12 | fontSize: 24,
13 | fontWeight: 'lighter',
14 | marginBottom: 20
15 | },
16 | paper: {
17 | padding: 30
18 | },
19 | clear: {
20 | clear: 'both'
21 | }
22 | };
23 |
24 | export default styles;
25 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/styles/theme-default.ts:
--------------------------------------------------------------------------------
1 | import { blue, pink, grey } from "@material-ui/core/colors";
2 | import { createTheme } from "@material-ui/core/styles";
3 |
4 | export const defaultTheme = createTheme({
5 | palette: {
6 | primary: blue,
7 | secondary: pink
8 | },
9 | // @ts-ignore
10 | appBar: {
11 | height: 57,
12 | color: blue[600]
13 | },
14 | drawer: {
15 | width: 230,
16 | color: grey[900]
17 | },
18 | raisedButton: {
19 | primaryColor: blue[600]
20 | }
21 | });
22 |
23 | export default defaultTheme;
24 |
--------------------------------------------------------------------------------
/ReactTemplate/content/client/views/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import LoginPage from './LoginPage';
3 | import AppLayout from './AppLayout';
4 | import auth from '../auth';
5 |
6 | export default function App() {
7 | const [authenticated, setAuthenticated] = React.useState(auth.hasAccessToken());
8 | const handleAuthenticated = () => setAuthenticated(true);
9 |
10 | return !authenticated ? : ;
11 | }
12 |
--------------------------------------------------------------------------------
/ReactTemplate/content/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "6.0.0",
3 | "name": "projectName__lower",
4 | "private": true,
5 | "scripts": {
6 | "build": "webpack",
7 | "build:Debug": "webpack --mode=development",
8 | "build:Release": "webpack --mode=production",
9 | "prod": "webpack -p --progress && dotnet publish -c Release",
10 | "analyze": "source-map-explorer 'wwwroot/dist/*.js'"
11 | },
12 | "dependencies": {
13 | "@material-ui/core": "^4.12.3",
14 | "@material-ui/icons": "^4.11.2",
15 | "chart.js": "^3.7.0",
16 | "dotnetify": "^5.2.2",
17 | "flexboxgrid": "~6.3.1",
18 | "react": "^17.0.2",
19 | "react-chartjs-2": "^4.0.0",
20 | "react-dom": "^17.0.2"
21 | },
22 | "devDependencies": {
23 | "@types/material-ui": "^0.21.12",
24 | "@types/node": "~14.0.5",
25 | "@types/react": "^17.0.38",
26 | "@types/react-dom": "^17.0.11",
27 | "aspnet-webpack": "^3.0.0",
28 | "css-loader": "~0.28.7",
29 | "mini-css-extract-plugin": "~0.4.0",
30 | "source-map-explorer": "^2.5.2",
31 | "style-loader": "~0.19.0",
32 | "svg-url-loader": "~2.3.2",
33 | "ts-loader": "^9.2.6",
34 | "typescript": "^4.5.4",
35 | "url-loader": "~0.6.2",
36 | "webpack": "^5.65.0",
37 | "webpack-cli": "^3.3.12",
38 | "webpack-dev-middleware": "^3.7.3",
39 | "webpack-hot-middleware": "~2.23.1"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ReactTemplate/content/projectName.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "projectName", "projectName.csproj", "{338DE673-32B6-4C59-8E23-E4C2965ACA55}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {338DE673-32B6-4C59-8E23-E4C2965ACA55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {338DE673-32B6-4C59-8E23-E4C2965ACA55}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {338DE673-32B6-4C59-8E23-E4C2965ACA55}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {338DE673-32B6-4C59-8E23-E4C2965ACA55}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {CC910A34-349D-4C85-AB3B-C366D361C442}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/ReactTemplate/content/server/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using BrunoLau.SpaServices.Webpack;
3 | using DotNetify;
4 | using DotNetify.Security;
5 | using Microsoft.IdentityModel.Tokens;
6 | using projectName;
7 |
8 | var builder = WebApplication.CreateBuilder(args);
9 |
10 | var services = builder.Services;
11 |
12 | // Add OpenID Connect server to produce JWT access tokens.
13 | services.AddAuthenticationServer();
14 |
15 | services.AddSignalR();
16 | services.AddDotNetify();
17 |
18 | services.AddTransient();
19 | services.AddSingleton();
20 |
21 | var app = builder.Build();
22 |
23 | app.UseAuthentication();
24 |
25 | app.UseWebSockets();
26 | app.UseDotNetify(config =>
27 | {
28 | // Middleware to do authenticate token in incoming request headers.
29 | config.UseJwtBearerAuthentication(new TokenValidationParameters
30 | {
31 | IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(AuthServer.SecretKey)),
32 | ValidateIssuerSigningKey = true,
33 | ValidateAudience = false,
34 | ValidateIssuer = false,
35 | ValidateLifetime = true,
36 | ClockSkew = TimeSpan.FromSeconds(0)
37 | });
38 |
39 | // Filter to check whether user has permission to access view models with [Authorize] attribute.
40 | config.UseFilter();
41 | });
42 |
43 | if (app.Environment.IsDevelopment())
44 | app.UseWebpackDevMiddlewareEx(new WebpackDevMiddlewareOptions { HotModuleReplacement = true });
45 |
46 | app.UseFileServer();
47 | app.UseRouting();
48 | app.UseEndpoints(endpoints =>
49 | {
50 | endpoints.MapHub("/dotnetify");
51 | endpoints.MapFallbackToFile("index.html");
52 | });
53 |
54 | app.Run();
--------------------------------------------------------------------------------
/ReactTemplate/content/server/Services/EmployeeService.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using Newtonsoft.Json;
3 |
4 | namespace projectName;
5 |
6 | public interface IEmployeeService
7 | {
8 | IList GetAll();
9 |
10 | EmployeeModel GetById(int id);
11 |
12 | int Add(EmployeeModel record);
13 |
14 | void Update(EmployeeModel record);
15 |
16 | void Delete(int id);
17 | }
18 |
19 | public class EmployeeModel
20 | {
21 | public int Id { get; set; }
22 | public string FirstName { get; set; }
23 | public string LastName { get; set; }
24 | public int ReportTo { get; set; }
25 | public string FullName => $"{FirstName} {LastName}";
26 | }
27 |
28 | public class EmployeeService : IEmployeeService
29 | {
30 | private List _employees;
31 | private int _newId;
32 |
33 | public EmployeeService()
34 | {
35 | _employees = JsonConvert.DeserializeObject>(this.GetEmbeddedResource("employees.json"));
36 | _newId = _employees.Count;
37 | }
38 |
39 | public IList GetAll() => _employees;
40 |
41 | public EmployeeModel GetById(int id) => _employees.FirstOrDefault(i => i.Id == id);
42 |
43 | public int Add(EmployeeModel record)
44 | {
45 | record.Id = ++_newId;
46 | _employees.Add(record);
47 | return record.Id;
48 | }
49 |
50 | public void Update(EmployeeModel record)
51 | {
52 | var idx = _employees.FindIndex(i => i.Id == record.Id);
53 | if (idx >= 0)
54 | _employees[idx] = record;
55 | }
56 |
57 | public void Delete(int id) => _employees.Remove(_employees.FirstOrDefault(i => i.Id == id));
58 |
59 | private string GetEmbeddedResource(string resourceName)
60 | {
61 | var assembly = GetType().Assembly;
62 | var name = assembly.GetManifestResourceNames().Where(i => i.EndsWith(resourceName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
63 | if (string.IsNullOrEmpty(name))
64 | throw new FileNotFoundException();
65 |
66 | using (var reader = new StreamReader(assembly.GetManifestResourceStream(name), Encoding.UTF8))
67 | return reader.ReadToEnd();
68 | }
69 | }
--------------------------------------------------------------------------------
/ReactTemplate/content/server/ViewModels/AppLayout.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using DotNetify;
3 | using DotNetify.Routing;
4 | using DotNetify.Security;
5 |
6 | namespace projectName;
7 |
8 | [Authorize]
9 | public class AppLayout : BaseVM, IRoutable
10 | {
11 | private enum Route
12 | {
13 | Home,
14 | Dashboard,
15 | FormPage,
16 | TablePage
17 | };
18 |
19 | public static string FormPagePath => "Form";
20 |
21 | public RoutingState RoutingState { get; set; }
22 |
23 | public object Menus => new List