initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
BASE_URL=https://portal.mydomain.com
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
**/*.js
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.env
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
yarn-error.log
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json .
|
||||||
|
COPY package-lock.json .
|
||||||
|
RUN npm install
|
||||||
|
COPY --from=builder /app/dist .
|
||||||
|
|
||||||
|
CMD ["node", "./server/server.js"]
|
||||||
Generated
+15981
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"name": "vpn-portal",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Portal for openvpn auth and profiles",
|
||||||
|
"scripts": {
|
||||||
|
"build": "run-s build:server build:web",
|
||||||
|
"build:server": "webpack --config webpack.server.production.js",
|
||||||
|
"build:web": "webpack --config webpack.web.production.js",
|
||||||
|
"build:server:once": "webpack --config webpack.server.development.js",
|
||||||
|
"dev:server": "npm run build:server:once && run-p nodemon:prod watch:server",
|
||||||
|
"watch:server": "webpack --config webpack.server.development.js --watch",
|
||||||
|
"nodemon:prod": "nodemon dist/server/server.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@webcomponents/webcomponentsjs": "^2.7.0",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"haunted": "^5.0.0",
|
||||||
|
"lit-html": "^2.6.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/webpack-dev-middleware": "^5.3.0",
|
||||||
|
"@types/webpack-hot-middleware": "^2.25.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
||||||
|
"@typescript-eslint/parser": "^5.51.0",
|
||||||
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"css-loader": "^6.7.3",
|
||||||
|
"dotenv-webpack": "^8.0.1",
|
||||||
|
"eslint": "^8.33.0",
|
||||||
|
"extract-loader": "^5.1.0",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"node-sass": "^8.0.0",
|
||||||
|
"nodemon": "^2.0.20",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"sass-loader": "^13.2.0",
|
||||||
|
"ts-loader": "^9.4.2",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"webpack": "^5.75.0",
|
||||||
|
"webpack-cli": "^5.0.1",
|
||||||
|
"webpack-dev-middleware": "^6.0.1",
|
||||||
|
"webpack-hot-middleware": "^2.25.3",
|
||||||
|
"webpack-merge": "^5.8.0",
|
||||||
|
"webpack-node-externals": "^3.0.0"
|
||||||
|
},
|
||||||
|
"nodemonConfig": {
|
||||||
|
"exec": "node -r dotenv/config",
|
||||||
|
"watch": [
|
||||||
|
"dist/server"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-explicit-any": 0,
|
||||||
|
"@typescript-eslint/explicit-function-return-type": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<script src="webcomponents-loader.js"></script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import express from "express";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import express from "express";
|
||||||
|
import router from "./router";
|
||||||
|
import webpack from "webpack";
|
||||||
|
import webpackDevMiddleware from "webpack-dev-middleware";
|
||||||
|
import webpackHotMiddleware from "webpack-hot-middleware";
|
||||||
|
import webpackConfig from "../../webpack.web.development.js";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const compiler = webpack(webpackConfig);
|
||||||
|
|
||||||
|
app.use(webpackDevMiddleware(compiler));
|
||||||
|
app.use(webpackHotMiddleware(compiler));
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
app.listen(8080, () => {
|
||||||
|
console.log("Server started");
|
||||||
|
});
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import express from "express";
|
||||||
|
import router from "./router";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(express.static(path.join(__dirname, "..", "web")));
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
const server = app.listen(8080, () => {
|
||||||
|
console.log("Server started");
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("SIGTERM", () => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { unsafeCSS, supportsAdoptingStyleSheets, CSSResult } from "lit-element";
|
||||||
|
import { useState, useEffect, useLayoutEffect, useMemo, useRef } from "haunted";
|
||||||
|
|
||||||
|
export function useStylesheet(initialStyles: CSSResult[] | CSSResult) {
|
||||||
|
const [needsShim, setNeedsShim] = useState(false);
|
||||||
|
const userStyles = useRef(initialStyles);
|
||||||
|
|
||||||
|
const styles = useMemo(() => {
|
||||||
|
let _styles: CSSResult[];
|
||||||
|
|
||||||
|
if (Array.isArray(userStyles.current)) {
|
||||||
|
// De-duplicate styles preserving the _last_ instance in the set.
|
||||||
|
// This is a performance optimization to avoid duplicated styles that can
|
||||||
|
// occur especially when composing via subclassing.
|
||||||
|
// The last item is kept to try to preserve the cascade order with the
|
||||||
|
// assumption that it's most important that last added styles override
|
||||||
|
// previous styles.
|
||||||
|
const addStyles = (styles: CSSResult[], set: Set<CSSResult>) =>
|
||||||
|
styles.reduceRight(
|
||||||
|
(set, s) =>
|
||||||
|
// Note: On IE set.add() does not return the set
|
||||||
|
Array.isArray(s) ? addStyles(s, set) : (set.add(s), set),
|
||||||
|
set
|
||||||
|
);
|
||||||
|
// Array.from does not work on Set in IE, otherwise return
|
||||||
|
// Array.from(addStyles(userStyles, new Set<CSSResult>())).reverse()
|
||||||
|
const set = addStyles(userStyles.current, new Set<CSSResult>());
|
||||||
|
const styles: CSSResult[] = [];
|
||||||
|
set.forEach((v) => styles.unshift(v));
|
||||||
|
_styles = styles;
|
||||||
|
} else {
|
||||||
|
_styles = userStyles.current === undefined ? [] : [userStyles.current];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that there are no invalid CSSStyleSheet instances here. They are
|
||||||
|
// invalid in two conditions.
|
||||||
|
// (1) the sheet is non-constructible (`sheet` of a HTMLStyleElement), but
|
||||||
|
// this is impossible to check except via .replaceSync or use
|
||||||
|
// (2) the ShadyCSS polyfill is enabled (:. supportsAdoptingStyleSheets is
|
||||||
|
// false)
|
||||||
|
return _styles.map((s) => {
|
||||||
|
if (s instanceof CSSStyleSheet && !supportsAdoptingStyleSheets) {
|
||||||
|
// Flatten the cssText from the passed constructible stylesheet (or
|
||||||
|
// undetectable non-constructible stylesheet). The user might have
|
||||||
|
// expected to update their stylesheets over time, but the alternative
|
||||||
|
// is a crash.
|
||||||
|
const cssText: string = Array.prototype.slice
|
||||||
|
.call(s.cssRules)
|
||||||
|
.reduce((css: string, rule: CSSRule) => css + rule.cssText, "");
|
||||||
|
return unsafeCSS(cssText);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
}, [userStyles.current]);
|
||||||
|
|
||||||
|
useLayoutEffect(
|
||||||
|
function () {
|
||||||
|
const elm = this.host as Element;
|
||||||
|
// There are three separate cases here based on Shadow DOM support.
|
||||||
|
// (1) shadowRoot polyfilled: use ShadyCSS
|
||||||
|
// (2) shadowRoot.adoptedStyleSheets available: use it
|
||||||
|
// (3) shadowRoot.adoptedStyleSheets polyfilled: append styles after
|
||||||
|
// rendering
|
||||||
|
if (
|
||||||
|
window["ShadyCSS"] !== undefined &&
|
||||||
|
!window["ShadyCSS"].nativeShadow
|
||||||
|
) {
|
||||||
|
window["ShadyCSS"].ScopingShim.prepareAdoptedCssText(
|
||||||
|
styles.map((s) => (s instanceof CSSResult ? s.cssText : "")),
|
||||||
|
elm.localName
|
||||||
|
);
|
||||||
|
} else if (supportsAdoptingStyleSheets) {
|
||||||
|
elm.shadowRoot!.adoptedStyleSheets = styles.map((s) =>
|
||||||
|
s instanceof CSSStyleSheet ? s : s.styleSheet!
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setNeedsShim(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[styles]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
function () {
|
||||||
|
const elm = this.host as Element;
|
||||||
|
if (needsShim) {
|
||||||
|
styles.forEach((s) => {
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = s.cssText;
|
||||||
|
elm.shadowRoot!.appendChild(style);
|
||||||
|
});
|
||||||
|
setNeedsShim(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[needsShim, styles]
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
:host {
|
||||||
|
font-family: Ubuntu, sans-serif;
|
||||||
|
display: block;
|
||||||
|
border: solid 1px black;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #77216f;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 0;
|
||||||
|
background-color: #aea79f;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { component, useState } from "haunted";
|
||||||
|
import { html, render } from "lit-html";
|
||||||
|
import { css, unsafeCSS } from "lit-element";
|
||||||
|
import { useStylesheet } from "./hooks/css";
|
||||||
|
import style from "./main.scss";
|
||||||
|
|
||||||
|
const CSS = css`
|
||||||
|
${unsafeCSS(style)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (module["hot"]) {
|
||||||
|
module["hot"].accept(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppComp = () => {
|
||||||
|
useStylesheet(CSS);
|
||||||
|
const [counter, setCounter] = useState(0);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<h1>My App</h1>
|
||||||
|
<p>Value = ${counter}</p>
|
||||||
|
<button
|
||||||
|
@click=${() => {
|
||||||
|
setCounter(counter + 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
increment
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
customElements.define("my-app", component(AppComp));
|
||||||
|
|
||||||
|
render(html`<my-app></my-app>`, document.body);
|
||||||
Vendored
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// typings.d.ts
|
||||||
|
declare module '*.scss' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.png' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.gif' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"removeComments": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": ["src/server", "src/shared"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"removeComments": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": ["src/web", "src/shared"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
exclude: [/node_modules/],
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: "ts-loader",
|
||||||
|
options: { configFile: "tsconfig.server.json" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: "server.js",
|
||||||
|
path: path.resolve(__dirname, "dist", "server"),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts", ".js"],
|
||||||
|
},
|
||||||
|
target: "node",
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const nodeExternals = require("webpack-node-externals");
|
||||||
|
const path = require("path");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
|
||||||
|
const common = require("./webpack.server.common.js");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
devtool: "inline-source-map",
|
||||||
|
entry: [path.join(__dirname, "src/server/server-dev.ts")],
|
||||||
|
externals: [ nodeExternals(), ],
|
||||||
|
mode: "development",
|
||||||
|
plugins: [new CleanWebpackPlugin()],
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const nodeExternals = require("webpack-node-externals");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const common = require("./webpack.server.common.js");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
devtool: "source-map",
|
||||||
|
node: { __dirname: false },
|
||||||
|
entry: [path.join(__dirname, "src/server/server.ts")],
|
||||||
|
externals: [nodeExternals()],
|
||||||
|
mode: "production",
|
||||||
|
plugins: [new CleanWebpackPlugin()],
|
||||||
|
});
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||||
|
const Dotenv = require("dotenv-webpack");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
target: "web",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
exclude: [/node_modules/],
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: "ts-loader",
|
||||||
|
options: { configFile: "tsconfig.web.json" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(css|scss)$/,
|
||||||
|
use: ["css-loader", "sass-loader"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg|gif)$/,
|
||||||
|
type: "asset/resource",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: "bundle.[name].js",
|
||||||
|
path: path.resolve(__dirname, "dist", "web"),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts", ".js"],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./src/index.ejs",
|
||||||
|
title: "My App",
|
||||||
|
}),
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
"./node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js",
|
||||||
|
{
|
||||||
|
from: "./node_modules/@webcomponents/webcomponentsjs/bundles",
|
||||||
|
to: "bundles",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new Dotenv(),
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const { merge, } = require("webpack-merge");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
|
||||||
|
const common = require("./webpack.web.common.js");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
devtool: "inline-source-map",
|
||||||
|
entry: ["webpack-hot-middleware/client?timeout=1000&reload=true", "./src/web/main.ts"],
|
||||||
|
mode: "development",
|
||||||
|
plugins: [
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const common = require("./webpack.web.common.js");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
// devtool: "source-map",
|
||||||
|
entry: [path.join(__dirname, "src/web/main.ts")],
|
||||||
|
mode: "production",
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user