A lot of new views
This commit is contained in:
commit
27b41f1bec
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@ -0,0 +1,8 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 3
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
public/bundle.*
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.rpt2_cache
|
||||
build/
|
||||
build.js
|
70
README.md
Normal file
70
README.md
Normal file
@ -0,0 +1,70 @@
|
||||
*Psst looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
|
||||
|
||||
---
|
||||
|
||||
# svelte app
|
||||
|
||||
This is a project template for [Svelte](https://svelte.technology) apps. It lives at https://github.com/sveltejs/template.
|
||||
|
||||
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
|
||||
|
||||
```bash
|
||||
npm install -g degit # you only need to do this once
|
||||
|
||||
degit sveltejs/template svelte-app
|
||||
cd svelte-app
|
||||
```
|
||||
|
||||
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Install the dependencies...
|
||||
|
||||
```bash
|
||||
cd svelte-app
|
||||
npm install
|
||||
```
|
||||
|
||||
...then start [Rollup](https://rollupjs.org):
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
|
||||
|
||||
|
||||
## Deploying to the web
|
||||
|
||||
### With [now](https://zeit.co/now)
|
||||
|
||||
Install `now` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g now
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
now
|
||||
```
|
||||
|
||||
As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
|
||||
|
||||
### With [surge](https://surge.sh/)
|
||||
|
||||
Install `surge` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g surge
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
surge public
|
||||
```
|
264
build.ts
Normal file
264
build.ts
Normal file
@ -0,0 +1,264 @@
|
||||
import * as rollup from "rollup";
|
||||
import * as svelte from 'rollup-plugin-svelte';
|
||||
import * as resolve from 'rollup-plugin-node-resolve';
|
||||
import * as commonjs from 'rollup-plugin-commonjs';
|
||||
import * as typescript from "rollup-plugin-typescript2";
|
||||
import * as fs from "fs";
|
||||
import * as copy from "rollup-plugin-copy-assets";
|
||||
|
||||
import {
|
||||
sass
|
||||
} from 'svelte-preprocess-sass';
|
||||
import {
|
||||
terser
|
||||
} from 'rollup-plugin-terser';
|
||||
|
||||
const production = process.argv.indexOf("-d") < 0;
|
||||
console.log(`Runnung in ${production ? "production" : "development"} mode!`);
|
||||
|
||||
let plg = [];
|
||||
|
||||
if (production) {
|
||||
plg.push(terser())
|
||||
}
|
||||
|
||||
if (!fs.existsSync("build"))
|
||||
fs.mkdirSync("build");
|
||||
|
||||
|
||||
const pages = ["Login", "Home", "User", "Public"];
|
||||
|
||||
let configs = pages.map(page => {
|
||||
const pageHtml = generateHtml(page)
|
||||
|
||||
fs.writeFileSync(`build/${page.toLowerCase()}.html`, pageHtml);
|
||||
|
||||
return <rollup.RollupOptions>{
|
||||
input: `./src/${page}/main.js`,
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: `build/${page.toLowerCase()}/bundle.js`
|
||||
},
|
||||
watch: {
|
||||
clearScreen: false
|
||||
},
|
||||
plugins: [
|
||||
(typescript as any)({
|
||||
tsconfig: "./src/tsconfig.json"
|
||||
}),
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
css: css => {
|
||||
css.write(`build/${page.toLowerCase()}/bundle.css`);
|
||||
},
|
||||
preprocess: {
|
||||
style: sass({
|
||||
includePaths: ['src', 'node_modules']
|
||||
})
|
||||
}
|
||||
}),
|
||||
(resolve as any)(),
|
||||
(commonjs as any)(),
|
||||
...plg
|
||||
]
|
||||
};
|
||||
})
|
||||
|
||||
import * as path from "path";
|
||||
|
||||
|
||||
function cssCopyPlugin() {
|
||||
return {
|
||||
name: 'css-copy', // this name will show up in warnings and errors
|
||||
resolveId(source) {
|
||||
if (source === 'virtual-module') {
|
||||
return source; // this signals that rollup should not ask other plugins or check the file system to find this id
|
||||
}
|
||||
return null; // other ids should be handled as usually
|
||||
},
|
||||
load(id) {
|
||||
if (id === 'virtual-module') {
|
||||
return 'export default "This is virtual!"'; // the source code for "virtual-module"
|
||||
}
|
||||
return null; // other ids should be handled as usually
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function generateHtml(pagename: string) {
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset='utf8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
|
||||
<title>OpenAuth - ${pagename}</title>
|
||||
|
||||
<link rel='stylesheet' href='global.css'>
|
||||
<link rel='stylesheet' href='${pagename.toLowerCase()}/bundle.css'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
<script src="global.js"></script>
|
||||
<script src='${pagename.toLowerCase()}/bundle.js'></script>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" lazyload>
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
var absolutePath = /^(?:\/|(?:[A-Za-z]:)?[\\|/])/;
|
||||
function isAbsolute(path) {
|
||||
return absolutePath.test(path);
|
||||
}
|
||||
|
||||
// function getAliasName(resolved, unresolved) {
|
||||
// var alias = path.basename(unresolved || resolved);
|
||||
// var ext = path.extname(resolved);
|
||||
// if (alias.endsWith(ext))
|
||||
// alias = alias.substr(0, alias.length - ext.length);
|
||||
// return alias;
|
||||
// }
|
||||
|
||||
function relativeId(id) {
|
||||
if (typeof process === 'undefined' || !isAbsolute(id))
|
||||
return id;
|
||||
return path.relative(process.cwd(), id);
|
||||
}
|
||||
|
||||
const tc: any = {
|
||||
enabled:
|
||||
process.env.FORCE_COLOR ||
|
||||
process.platform === "win32" ||
|
||||
(process.stdout.isTTY && process.env.TERM && process.env.TERM !== "dumb")
|
||||
};
|
||||
const Styles = (tc.Styles = {});
|
||||
const defineProp = Object.defineProperty;
|
||||
|
||||
const init = (style, open, close, re) => {
|
||||
let i,
|
||||
len = 1,
|
||||
seq = [(Styles[style] = { open, close, re })];
|
||||
|
||||
const fn = s => {
|
||||
if (tc.enabled) {
|
||||
for (i = 0, s += ""; i < len; i++) {
|
||||
style = seq[i];
|
||||
s =
|
||||
(open = style.open) +
|
||||
(~s.indexOf((close = style.close), 4) // skip first \x1b[
|
||||
? s.replace(style.re, open)
|
||||
: s) +
|
||||
close;
|
||||
}
|
||||
len = 1;
|
||||
}
|
||||
return s
|
||||
};
|
||||
|
||||
defineProp(tc, style, {
|
||||
get: () => {
|
||||
for (let k in Styles)
|
||||
defineProp(fn, k, {
|
||||
get: () => ((seq[len++] = Styles[k]), fn)
|
||||
});
|
||||
delete tc[style];
|
||||
return (tc[style] = fn)
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
init("reset", "\x1b[0m", "\x1b[0m", /\x1b\[0m/g);
|
||||
init("bold", "\x1b[1m", "\x1b[22m", /\x1b\[22m/g);
|
||||
init("dim", "\x1b[2m", "\x1b[22m", /\x1b\[22m/g);
|
||||
init("italic", "\x1b[3m", "\x1b[23m", /\x1b\[23m/g);
|
||||
init("underline", "\x1b[4m", "\x1b[24m", /\x1b\[24m/g);
|
||||
init("inverse", "\x1b[7m", "\x1b[27m", /\x1b\[27m/g);
|
||||
init("hidden", "\x1b[8m", "\x1b[28m", /\x1b\[28m/g);
|
||||
init("strikethrough", "\x1b[9m", "\x1b[29m", /\x1b\[29m/g);
|
||||
init("black", "\x1b[30m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("red", "\x1b[31m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("green", "\x1b[32m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("yellow", "\x1b[33m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("blue", "\x1b[34m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("magenta", "\x1b[35m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("cyan", "\x1b[36m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("white", "\x1b[37m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("gray", "\x1b[90m", "\x1b[39m", /\x1b\[39m/g);
|
||||
init("bgBlack", "\x1b[40m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgRed", "\x1b[41m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgGreen", "\x1b[42m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgYellow", "\x1b[43m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgBlue", "\x1b[44m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgMagenta", "\x1b[45m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgCyan", "\x1b[46m", "\x1b[49m", /\x1b\[49m/g);
|
||||
init("bgWhite", "\x1b[47m", "\x1b[49m", /\x1b\[49m/g);
|
||||
|
||||
const turbocolor: any = tc;
|
||||
|
||||
|
||||
function handleError(err, recover) {
|
||||
if (recover === void 0) { recover = false; }
|
||||
var description = err.message || err;
|
||||
if (err.name)
|
||||
description = err.name + ": " + description;
|
||||
var message = (err.plugin
|
||||
? "(" + err.plugin + " plugin) " + description
|
||||
: description) || err;
|
||||
console.error(turbocolor.bold.red("[!] " + turbocolor.bold(message.toString())));
|
||||
if (err.url) {
|
||||
console.error(turbocolor.cyan(err.url));
|
||||
}
|
||||
if (err.loc) {
|
||||
console.error(relativeId(err.loc.file || err.id) + " (" + err.loc.line + ":" + err.loc.column + ")");
|
||||
}
|
||||
else if (err.id) {
|
||||
console.error(relativeId(err.id));
|
||||
}
|
||||
if (err.frame) {
|
||||
console.error(turbocolor.dim(err.frame));
|
||||
}
|
||||
if (err.stack) {
|
||||
|
||||
//console.error(turbocolor.dim(err.stack));
|
||||
}
|
||||
console.error('');
|
||||
if (!recover)
|
||||
process.exit(1);
|
||||
}
|
||||
let start: [number, number];
|
||||
if (process.argv.indexOf("-w") >= 0) {
|
||||
rollup.watch(configs).on("event", event => {
|
||||
if (event.code === "BUNDLE_START") {
|
||||
start = process.hrtime();
|
||||
} else if (event.code === "BUNDLE_END") {
|
||||
let diff = process.hrtime(start);
|
||||
console.log(`--- Took ${diff[0] * 1000 + diff[1] / 1000000}ms`);
|
||||
} else if (event.code === "ERROR") {
|
||||
// console.error(event.error);
|
||||
handleError(event.error, true);
|
||||
} else if (event.code === "FATAL") {
|
||||
handleError(event.error, true);
|
||||
} else {
|
||||
console.log(event);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
start = process.hrtime();
|
||||
Promise.all(configs.map(config => {
|
||||
return rollup.rollup(config).then((value) => {
|
||||
value.generate(config as rollup.OutputOptions)
|
||||
}).catch(err => {
|
||||
handleError(err, true);
|
||||
// console.error(err);
|
||||
})
|
||||
})).then(vals => {
|
||||
let diff = process.hrtime(start);
|
||||
console.log(`--- Took ${diff[0] * 1000 + diff[1] / 1000000}ms`);
|
||||
})
|
||||
}
|
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"node-sass": "^4.12.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^1.11.3",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-copy-assets": "^2.0.1",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"rollup-plugin-terser": "^4.0.4",
|
||||
"sirv-cli": "^0.4.0",
|
||||
"svelte": "^3.2.1",
|
||||
"svelte-preprocess-sass": "^0.2.0",
|
||||
"typescript": "^3.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublishOnly": "npm run build",
|
||||
"build": "node build.js",
|
||||
"autobuild": "node build.js -w -d",
|
||||
"dev": "run-p start:dev autobuild",
|
||||
"start": "sirv public",
|
||||
"start:dev": "sirv public --dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hibas123/utils": "^2.1.0",
|
||||
"rollup-plugin-typescript2": "^0.21.0",
|
||||
"what-the-pack": "^2.0.3"
|
||||
}
|
||||
}
|
3
public/font.css
Normal file
3
public/font.css
Normal file
@ -0,0 +1,3 @@
|
||||
* {
|
||||
font-family: 'Roboto', sans-serif !important;
|
||||
}
|
245
public/global.css
Normal file
245
public/global.css
Normal file
@ -0,0 +1,245 @@
|
||||
:root {
|
||||
--primary: #1E88E5;
|
||||
--mdc-theme-primary: var(--primary);
|
||||
--mdc-theme-primary-bg: var(--mdc-theme--primary);
|
||||
--mdc-theme-on-primary: white;
|
||||
--error: #ff2f00;
|
||||
--border-color: #ababab;
|
||||
|
||||
--default-font-size: 1.05rem;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Roboto', 'Helvetica', sans-serif;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
color: #636363;
|
||||
position: relative;
|
||||
background: #eee;
|
||||
height: 100%;
|
||||
font-size: var(--default-font-size);
|
||||
}
|
||||
|
||||
.group {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
.floating>input {
|
||||
font-size: 1.2rem;
|
||||
padding: 10px 10px 10px 5px;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
display: block;
|
||||
background: #fafafa;
|
||||
background: unset;
|
||||
color: #636363;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
/* border-bottom: 1px solid #757575; */
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.floating>input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Label */
|
||||
|
||||
.floating>label {
|
||||
color: #999;
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 5px;
|
||||
top: 10px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* active */
|
||||
|
||||
.floating>input:focus~label,
|
||||
.floating>input.used~label {
|
||||
top: -.75em;
|
||||
transform: scale(.75);
|
||||
left: -2px;
|
||||
/* font-size: 14px; */
|
||||
color: var(--primary);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
/* Underline */
|
||||
|
||||
.bar {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bar:before,
|
||||
.bar:after {
|
||||
content: '';
|
||||
height: 2px;
|
||||
width: 0;
|
||||
bottom: 1px;
|
||||
position: absolute;
|
||||
background: var(--primary);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.bar:before {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.bar:after {
|
||||
right: 50%;
|
||||
}
|
||||
|
||||
/* active */
|
||||
|
||||
.floating>input:focus~.bar:before,
|
||||
.floating>input:focus~.bar:after {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* Highlight */
|
||||
|
||||
.highlight {
|
||||
position: absolute;
|
||||
height: 60%;
|
||||
width: 100px;
|
||||
top: 25%;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* active */
|
||||
|
||||
.floating>input:focus~.highlight {
|
||||
animation: inputHighlighter 0.3s ease;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
@keyframes inputHighlighter {
|
||||
from {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
to {
|
||||
width: 0;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
position: relative;
|
||||
|
||||
display: block;
|
||||
margin: 2rem;
|
||||
padding: 0 1em;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
border-width: 0;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
|
||||
|
||||
background-color: #cccccc;
|
||||
color: #ecf0f1;
|
||||
|
||||
transition: background-color .3s;
|
||||
|
||||
height: 48px;
|
||||
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.btn:hover,
|
||||
.btn:focus {
|
||||
filter: brightness(90%);
|
||||
}
|
||||
|
||||
.btn>* {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn span {
|
||||
display: block;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.btn:before {
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
||||
display: block;
|
||||
width: 0;
|
||||
padding-top: 0;
|
||||
|
||||
border-radius: 100%;
|
||||
|
||||
background-color: rgba(236, 240, 241, .3);
|
||||
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
-moz-transform: translate(-50%, -50%);
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
-o-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.btn:active:before {
|
||||
width: 120%;
|
||||
padding-top: 120%;
|
||||
|
||||
transition: width .2s ease-out, padding-top .2s ease-out;
|
||||
}
|
||||
|
||||
.loader_box {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.loader:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin: 1px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid var(--primary);
|
||||
border-color: var(--primary) transparent var(--primary) transparent;
|
||||
animation: loader 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
35
public/global.js
Normal file
35
public/global.js
Normal file
@ -0,0 +1,35 @@
|
||||
(() => {
|
||||
const elements = new WeakSet();
|
||||
|
||||
function check() {
|
||||
document.querySelectorAll(".floating>input").forEach(e => {
|
||||
if (elements.has(e)) return;
|
||||
elements.add(e);
|
||||
|
||||
function checkState() {
|
||||
console.log("Check State");
|
||||
if (e.value !== "") {
|
||||
if (e.classList.contains("used")) return;
|
||||
e.classList.add("used")
|
||||
} else {
|
||||
if (e.classList.contains("used")) e.classList.remove("used")
|
||||
}
|
||||
}
|
||||
|
||||
e.addEventListener("change", () => checkState())
|
||||
checkState()
|
||||
})
|
||||
};
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
check();
|
||||
});
|
||||
|
||||
|
||||
// Start observing the target node for configured mutations
|
||||
observer.observe(window.document, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
check();
|
||||
})()
|
3
public/home/bundle.css
Normal file
3
public/home/bundle.css
Normal file
@ -0,0 +1,3 @@
|
||||
.main.svelte-o924iy{padding:2rem}li.svelte-o924iy{list-style:none;padding:1rem}li.svelte-o924iy>a.svelte-o924iy{text-decoration:none}
|
||||
|
||||
/*# sourceMappingURL=bundle.css.map */
|
12
public/home/bundle.css.map
Normal file
12
public/home/bundle.css.map
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 3,
|
||||
"file": "bundle.css",
|
||||
"sources": [
|
||||
"../../src/Home/App.svelte"
|
||||
],
|
||||
"sourcesContent": [
|
||||
"<style>\n .main {\n padding: 2rem;\n }\n\n li {\n list-style: none;\n padding: 1rem;\n }\n\n li>a {\n text-decoration: none;\n }\n</style>\n\n<div class=\"main\">\n <h1>Home Page</h1>\n\n <h2>About</h2>\n <p>\n OpenAuth is a Service to provide simple Authentication to a veriaty of Applications.\n With a simple to use API and different Strategies, it can be easily integrated\n into most Applications.\n </p>\n\n <h2>QickLinks</h2>\n <p>\n If you want to manage your Account, click <a href=\"user.html\">here</a>\n </p>\n\n <h2> Applications using OpenAuth </h2>\n\n <ul>\n <li><a href=\"https://ebook.stamm.me\">EBook Store and Reader</a></li>\n <li><a href=\"https://notes.hibas123.de\">Secure and Simple Notes application</a></li>\n </ul>\n</div>"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "AACG,KAAK,cAAC,CAAC,AACJ,OAAO,CAAE,IAAI,AAChB,CAAC,AAED,EAAE,cAAC,CAAC,AACD,UAAU,CAAE,IAAI,CAChB,OAAO,CAAE,IAAI,AAChB,CAAC,AAED,gBAAE,CAAC,CAAC,cAAC,CAAC,AACH,eAAe,CAAE,IAAI,AACxB,CAAC"
|
||||
}
|
1828
public/home/bundle.js
Normal file
1828
public/home/bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/home/bundle.js.map
Normal file
1
public/home/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
21
public/index.html
Normal file
21
public/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset='utf8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
|
||||
<title>OpenAuth - Home</title>
|
||||
|
||||
<link rel='stylesheet' href='global.css'>
|
||||
<link rel='stylesheet' href='home/bundle.css'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
<script src='home/bundle.js'></script>
|
||||
<script src="global.js"></script>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" lazyload>
|
||||
</body>
|
||||
|
||||
</html>
|
21
public/login.html
Normal file
21
public/login.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset='utf8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
|
||||
<title>OpenAuth - Login</title>
|
||||
|
||||
<link rel='stylesheet' href='global.css'>
|
||||
<link rel='stylesheet' href='login/bundle.css'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
<script src='login/bundle.js'></script>
|
||||
<script src="global.js"></script>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" lazyload>
|
||||
</body>
|
||||
|
||||
</html>
|
11
public/login/bundle.css
Normal file
11
public/login/bundle.css
Normal file
File diff suppressed because one or more lines are too long
28
public/login/bundle.css.map
Normal file
28
public/login/bundle.css.map
Normal file
File diff suppressed because one or more lines are too long
6197
public/login/bundle.js
Normal file
6197
public/login/bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
public/login/bundle.js.map
Normal file
1
public/login/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
202
public/roboto/LICENSE.txt
Normal file
202
public/roboto/LICENSE.txt
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
BIN
public/roboto/Roboto-Black.ttf
Normal file
BIN
public/roboto/Roboto-Black.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-BlackItalic.ttf
Normal file
BIN
public/roboto/Roboto-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Bold.ttf
Normal file
BIN
public/roboto/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-BoldItalic.ttf
Normal file
BIN
public/roboto/Roboto-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Italic.ttf
Normal file
BIN
public/roboto/Roboto-Italic.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Light.ttf
Normal file
BIN
public/roboto/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-LightItalic.ttf
Normal file
BIN
public/roboto/Roboto-LightItalic.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Medium.ttf
Normal file
BIN
public/roboto/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-MediumItalic.ttf
Normal file
BIN
public/roboto/Roboto-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Regular.ttf
Normal file
BIN
public/roboto/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Regular.woff2
Normal file
BIN
public/roboto/Roboto-Regular.woff2
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-Thin.ttf
Normal file
BIN
public/roboto/Roboto-Thin.ttf
Normal file
Binary file not shown.
BIN
public/roboto/Roboto-ThinItalic.ttf
Normal file
BIN
public/roboto/Roboto-ThinItalic.ttf
Normal file
Binary file not shown.
0
public/serviceworker.js
Normal file
0
public/serviceworker.js
Normal file
21
public/user.html
Normal file
21
public/user.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset='utf8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
|
||||
<title>OpenAuth - User</title>
|
||||
|
||||
<link rel='stylesheet' href='global.css'>
|
||||
<link rel='stylesheet' href='user/bundle.css'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content" style="height:100%"></div>
|
||||
<script src='user/bundle.js'></script>
|
||||
<script src="global.js"></script>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" lazyload>
|
||||
</body>
|
||||
|
||||
</html>
|
12
public/user.old/bundle.css
Normal file
12
public/user.old/bundle.css
Normal file
@ -0,0 +1,12 @@
|
||||
.loading.svelte-1ee89l3{background-color:rgba(0, 0, 0, 0.04);filter:blur(10px)}:root{--sidebar-width:250px}.root.svelte-1ee89l3{height:100%}.container.svelte-1ee89l3{display:grid;height:100%;grid-template-columns:auto 100%;grid-template-rows:60px auto 60px;grid-template-areas:"sidebar header"
|
||||
"sidebar mc"
|
||||
"sidebar footer"
|
||||
}.header.svelte-1ee89l3{grid-area:header;background-color:var(--primary);padding:12px;display:flex}.header.svelte-1ee89l3>h1.svelte-1ee89l3{margin:0;padding:0;font-size:24px;line-height:36px;color:white;margin-left:2rem}.header.svelte-1ee89l3>button.svelte-1ee89l3{height:36px;background-color:transparent;border:none;font-size:20px}.header.svelte-1ee89l3>button.svelte-1ee89l3:hover{background-color:rgba(255, 255, 255, 0.151)
|
||||
}.sidebar.svelte-1ee89l3{width:0;overflow:hidden;grid-area:sidebar;transition:width .2s;background-color:lightgrey;height:100%}.sidebar-visible.svelte-1ee89l3{width:var(--sidebar-width);transition:width .2s;box-shadow:10px 0px 10px 2px rgba(0, 0, 0, 0.52)}.content.svelte-1ee89l3{grid-area:mc;padding:1rem}.footer.svelte-1ee89l3{grid-area:footer}@media(min-width: 45rem){.container.svelte-1ee89l3{grid-template-columns:auto 1fr}.sidebar.svelte-1ee89l3{width:var(--sidebar-width);transition:all .2s;box-shadow:10px 0px 10px 2px rgba(0, 0, 0, 0.52)}.content.svelte-1ee89l3{padding:2rem}}.loader_container.svelte-1ee89l3{position:absolute;display:flex;flex-direction:column;justify-content:center;top:0;bottom:0;left:0;right:0;z-index:2}
|
||||
:root{--rel-size:0.75rem}.container.svelte-zltjsn{height:calc(var(--rel-size) * 3);padding:var(--rel-size);display:flex;align-items:center}.icon.svelte-zltjsn{width:calc(var(--rel-size) * 3);height:calc(var(--rel-size) * 3)}.icon.svelte-zltjsn>img.svelte-zltjsn{width:calc(var(--rel-size) * 3);height:calc(var(--rel-size) * 3)}.title.svelte-zltjsn{margin-left:var(--rel-size)}
|
||||
.btn.svelte-1lc4uv{background-color:var(--primary);margin:auto 0;margin-left:1rem;font-size:1rem;padding:0 0.5rem}.floating.svelte-1lc4uv{margin-bottom:0}.input-container.svelte-1lc4uv{display:flex}.input-container.svelte-1lc4uv>.svelte-1lc4uv:first-child{flex-grow:1}select.svelte-1lc4uv{background-color:unset;border:0;border-radius:0;color:unset;font-size:unset;border-bottom:1px solid #757575;-moz-appearance:none;-webkit-appearance:none;appearance:none;height:100%;width:100%}select.svelte-1lc4uv>option.svelte-1lc4uv{background-color:unset}.select-wrapper.svelte-1lc4uv{position:relative}.select-wrapper.svelte-1lc4uv::after{content:">";display:block;position:absolute;right:2rem;top:0;bottom:0;width:1rem;transform:rotate(90deg) scaleY(2)}.error.svelte-1lc4uv{color:var(--error)}
|
||||
.btn.svelte-hfyfkx{background-color:var(--primary);margin:auto 0;margin-left:1rem;font-size:1rem;padding:0 0.5rem}.input-container>.svelte-hfyfkx:first-child{flex-grow:1}
|
||||
.box.svelte-yv48ir{border-radius:4px;box-shadow:0 8px 12px rgba(0, 0, 0, 0.30), 0 5px 4px rgba(0, 0, 0, 0.22);padding:2rem;margin-bottom:1rem;background-color:white}.box.svelte-yv48ir>h1{margin:0;margin-bottom:1rem;color:#444444;font-size:1.3rem}.box.svelte-yv48ir>div{padding:16px;border-top:1px solid var(--border-color);word-wrap:break-word}.box.svelte-yv48ir>div:first-of-type{border-top:none}@media(min-width: 45rem){.box.svelte-yv48ir{margin-bottom:2rem}}
|
||||
.root.svelte-1h07xvd:hover{background-color:rgba(0, 0, 0, 0.04)}.container.svelte-1h07xvd{display:flex;flex-direction:row}.values.svelte-1h07xvd{flex-grow:1;display:flex;flex-direction:column;max-width:calc(100% - var(--default-font-size) - 16px)}.values.svelte-1h07xvd>div.svelte-1h07xvd:first-child{transform-origin:left;transform:scale(0.95);margin-right:24px;font-weight:500}.values.svelte-1h07xvd>div.svelte-1h07xvd:nth-child(2){color:black}svg{margin:auto 8px auto 8px;height:var(--default-font-size);min-width:var(--default-font-size)}.body.svelte-1h07xvd{box-sizing:border-box;padding:.1px;margin-top:2rem}@media(min-width: 45rem){.values.svelte-1h07xvd{flex-direction:row}.values.svelte-1h07xvd>div.svelte-1h07xvd:first-child{transform:unset;flex-basis:120px;min-width:120px}}.highlight-element.svelte-1h07xvd{background-color:#7bff003b}
|
||||
|
||||
/*# sourceMappingURL=bundle.css.map */
|
22
public/user.old/bundle.css.map
Normal file
22
public/user.old/bundle.css.map
Normal file
File diff suppressed because one or more lines are too long
4588
public/user.old/bundle.js
Normal file
4588
public/user.old/bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/user.old/bundle.js.map
Normal file
1
public/user.old/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
12
public/user/bundle.css
Normal file
12
public/user/bundle.css
Normal file
@ -0,0 +1,12 @@
|
||||
.loading.svelte-1ee89l3{background-color:rgba(0, 0, 0, 0.04);filter:blur(10px)}:root{--sidebar-width:250px}.root.svelte-1ee89l3{height:100%}.container.svelte-1ee89l3{display:grid;height:100%;grid-template-columns:auto 100%;grid-template-rows:60px auto 60px;grid-template-areas:"sidebar header"
|
||||
"sidebar mc"
|
||||
"sidebar footer"
|
||||
}.header.svelte-1ee89l3{grid-area:header;background-color:var(--primary);padding:12px;display:flex}.header.svelte-1ee89l3>h1.svelte-1ee89l3{margin:0;padding:0;font-size:24px;line-height:36px;color:white;margin-left:2rem}.header.svelte-1ee89l3>button.svelte-1ee89l3{height:36px;background-color:transparent;border:none;font-size:20px}.header.svelte-1ee89l3>button.svelte-1ee89l3:hover{background-color:rgba(255, 255, 255, 0.151)
|
||||
}.sidebar.svelte-1ee89l3{width:0;overflow:hidden;grid-area:sidebar;transition:width .2s;background-color:lightgrey;height:100%}.sidebar-visible.svelte-1ee89l3{width:var(--sidebar-width);transition:width .2s;box-shadow:10px 0px 10px 2px rgba(0, 0, 0, 0.52)}.content.svelte-1ee89l3{grid-area:mc;padding:1rem}.footer.svelte-1ee89l3{grid-area:footer}@media(min-width: 45rem){.container.svelte-1ee89l3{grid-template-columns:auto 1fr}.sidebar.svelte-1ee89l3{width:var(--sidebar-width);transition:all .2s;box-shadow:10px 0px 10px 2px rgba(0, 0, 0, 0.52)}.content.svelte-1ee89l3{padding:2rem}}.loader_container.svelte-1ee89l3{position:absolute;display:flex;flex-direction:column;justify-content:center;top:0;bottom:0;left:0;right:0;z-index:2}
|
||||
:root{--rel-size:0.75rem}.container.svelte-zltjsn{height:calc(var(--rel-size) * 3);padding:var(--rel-size);display:flex;align-items:center}.icon.svelte-zltjsn{width:calc(var(--rel-size) * 3);height:calc(var(--rel-size) * 3)}.icon.svelte-zltjsn>img.svelte-zltjsn{width:calc(var(--rel-size) * 3);height:calc(var(--rel-size) * 3)}.title.svelte-zltjsn{margin-left:var(--rel-size)}
|
||||
.btn.svelte-1lc4uv{background-color:var(--primary);margin:auto 0;margin-left:1rem;font-size:1rem;padding:0 0.5rem}.floating.svelte-1lc4uv{margin-bottom:0}.input-container.svelte-1lc4uv{display:flex}.input-container.svelte-1lc4uv>.svelte-1lc4uv:first-child{flex-grow:1}select.svelte-1lc4uv{background-color:unset;border:0;border-radius:0;color:unset;font-size:unset;border-bottom:1px solid #757575;-moz-appearance:none;-webkit-appearance:none;appearance:none;height:100%;width:100%}select.svelte-1lc4uv>option.svelte-1lc4uv{background-color:unset}.select-wrapper.svelte-1lc4uv{position:relative}.select-wrapper.svelte-1lc4uv::after{content:">";display:block;position:absolute;right:2rem;top:0;bottom:0;width:1rem;transform:rotate(90deg) scaleY(2)}.error.svelte-1lc4uv{color:var(--error)}
|
||||
.btn.svelte-hfyfkx{background-color:var(--primary);margin:auto 0;margin-left:1rem;font-size:1rem;padding:0 0.5rem}.input-container>.svelte-hfyfkx:first-child{flex-grow:1}
|
||||
.box.svelte-yv48ir{border-radius:4px;box-shadow:0 8px 12px rgba(0, 0, 0, 0.30), 0 5px 4px rgba(0, 0, 0, 0.22);padding:2rem;margin-bottom:1rem;background-color:white}.box.svelte-yv48ir>h1{margin:0;margin-bottom:1rem;color:#444444;font-size:1.3rem}.box.svelte-yv48ir>div{padding:16px;border-top:1px solid var(--border-color);word-wrap:break-word}.box.svelte-yv48ir>div:first-of-type{border-top:none}@media(min-width: 45rem){.box.svelte-yv48ir{margin-bottom:2rem}}
|
||||
.root.svelte-1h07xvd:hover{background-color:rgba(0, 0, 0, 0.04)}.container.svelte-1h07xvd{display:flex;flex-direction:row}.values.svelte-1h07xvd{flex-grow:1;display:flex;flex-direction:column;max-width:calc(100% - var(--default-font-size) - 16px)}.values.svelte-1h07xvd>div.svelte-1h07xvd:first-child{transform-origin:left;transform:scale(0.95);margin-right:24px;font-weight:500}.values.svelte-1h07xvd>div.svelte-1h07xvd:nth-child(2){color:black}svg{margin:auto 8px auto 8px;height:var(--default-font-size);min-width:var(--default-font-size)}.body.svelte-1h07xvd{box-sizing:border-box;padding:.1px;margin-top:2rem}@media(min-width: 45rem){.values.svelte-1h07xvd{flex-direction:row}.values.svelte-1h07xvd>div.svelte-1h07xvd:first-child{transform:unset;flex-basis:120px;min-width:120px}}.highlight-element.svelte-1h07xvd{background-color:#7bff003b}
|
||||
|
||||
/*# sourceMappingURL=bundle.css.map */
|
22
public/user/bundle.css.map
Normal file
22
public/user/bundle.css.map
Normal file
File diff suppressed because one or more lines are too long
4695
public/user/bundle.js
Normal file
4695
public/user/bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/user/bundle.js.map
Normal file
1
public/user/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
37
src/Home/App.svelte
Normal file
37
src/Home/App.svelte
Normal file
@ -0,0 +1,37 @@
|
||||
<style>
|
||||
.main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
li>a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="main">
|
||||
<h1>Home Page</h1>
|
||||
|
||||
<h2>About</h2>
|
||||
<p>
|
||||
OpenAuth is a Service to provide simple Authentication to a veriaty of Applications.
|
||||
With a simple to use API and different Strategies, it can be easily integrated
|
||||
into most Applications.
|
||||
</p>
|
||||
|
||||
<h2>QickLinks</h2>
|
||||
<p>
|
||||
If you want to manage your Account, click <a href="user.html">here</a>
|
||||
</p>
|
||||
|
||||
<h2> Applications using OpenAuth </h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="https://ebook.stamm.me">EBook Store and Reader</a></li>
|
||||
<li><a href="https://notes.hibas123.de">Secure and Simple Notes application</a></li>
|
||||
</ul>
|
||||
</div>
|
7
src/Home/main.js
Normal file
7
src/Home/main.js
Normal file
@ -0,0 +1,7 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
var app = new App({
|
||||
target: document.getElementById("content")
|
||||
});
|
||||
|
||||
export default app;
|
206
src/Login/App.svelte
Normal file
206
src/Login/App.svelte
Normal file
@ -0,0 +1,206 @@
|
||||
<script>
|
||||
import Api from "./api";
|
||||
import Credentials from "./Credentials.svelte";
|
||||
import Redirect from "./Redirect.svelte";
|
||||
import Twofactor from "./Twofactor.svelte";
|
||||
|
||||
const appname = "OpenAuth";
|
||||
|
||||
const states = {
|
||||
credentials: 1,
|
||||
twofactor: 3,
|
||||
redirect: 4
|
||||
}
|
||||
|
||||
let username = Api.getUsername();
|
||||
let password = "";
|
||||
|
||||
let loading = false;
|
||||
let state = states.credentials;
|
||||
|
||||
function getButtonText(state) {
|
||||
switch (state) {
|
||||
case states.username:
|
||||
return "Next";
|
||||
case states.password:
|
||||
return "Login";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
$: btnText = getButtonText(state);
|
||||
|
||||
let error;
|
||||
|
||||
// window.addEventListener("popstate", () => {
|
||||
// state = history.state;
|
||||
// })
|
||||
|
||||
|
||||
function LoadRedirect() {
|
||||
state = states.redirect;
|
||||
}
|
||||
|
||||
function Loading() {
|
||||
state = states.loading;
|
||||
}
|
||||
|
||||
|
||||
let salt;
|
||||
async function buttonClick() {
|
||||
if (state === states.username) {
|
||||
Loading()
|
||||
let res = await Api.setUsername(username);
|
||||
if (res.error) {
|
||||
error = res.error;
|
||||
LoadUsername();
|
||||
} else {
|
||||
LoadPassword();
|
||||
}
|
||||
|
||||
} else if (state === states.password) {
|
||||
Loading();
|
||||
let res = await Api.setPassword(password);
|
||||
if (res.error) {
|
||||
error = res.error;
|
||||
LoadPassword();
|
||||
} else {
|
||||
if (res.tfa) {
|
||||
// TODO: Make TwoFactor UI/-s
|
||||
} else {
|
||||
LoadRedirect();
|
||||
}
|
||||
}
|
||||
btnText = "Error";
|
||||
}
|
||||
}
|
||||
|
||||
function startRedirect() {
|
||||
state = states.redirect;
|
||||
// Show message to User and then redirect
|
||||
setTimeout(() => Api.finish(), 2000);
|
||||
}
|
||||
|
||||
function afterCredentials() {
|
||||
Object.keys(Api); // Some weird bug needs this???
|
||||
|
||||
if (Api.twofactor) {
|
||||
state = states.twofactor;
|
||||
} else {
|
||||
startRedirect();
|
||||
}
|
||||
}
|
||||
|
||||
function afterTwoFactor() {
|
||||
startRedirect();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="form-container">
|
||||
<form action="JavaScript:void(0)" class="card">
|
||||
<div class="card title-container">
|
||||
<h1>Login</h1>
|
||||
</div>
|
||||
{#if loading}
|
||||
<div class="loader_container">
|
||||
<div class="loader_box">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="container" class:loading_container={loading}>
|
||||
{#if state === states.redirect}
|
||||
<Redirect/>
|
||||
{:else if state === states.credentials}
|
||||
<Credentials next={afterCredentials} setLoading={s => loading = s} />
|
||||
{:else if state === states.twofactor}
|
||||
<Twofactor finish={afterTwoFactor} setLoading={s => loading = s} />
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<footer>
|
||||
<p>Powered by {appname}</p>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22);
|
||||
position: relative;
|
||||
padding: 1px;
|
||||
background-color: white !important;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
|
||||
.container {
|
||||
overflow: hidden;
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.floating {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
h3 {
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
margin: -30px auto 0 auto;
|
||||
max-width: 250px;
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.loading_container {
|
||||
filter: blur(1px) opacity(50%);
|
||||
|
||||
}
|
||||
|
||||
.loader_container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
79
src/Login/Credentials.svelte
Normal file
79
src/Login/Credentials.svelte
Normal file
@ -0,0 +1,79 @@
|
||||
<script>
|
||||
import Api from "./api";
|
||||
|
||||
let error;
|
||||
let password = "";
|
||||
let username = Api.getUsername();
|
||||
|
||||
const states = {
|
||||
username: 1,
|
||||
password: 2
|
||||
};
|
||||
|
||||
let state = states.username;
|
||||
|
||||
let salt;
|
||||
|
||||
export let setLoading;
|
||||
export let next;
|
||||
|
||||
async function buttonClick() {
|
||||
setLoading(true);
|
||||
if (state === states.username) {
|
||||
let res = await Api.setUsername(username);
|
||||
if (res.error) {
|
||||
error = res.error;
|
||||
} else {
|
||||
state = states.password;
|
||||
error = undefined;
|
||||
}
|
||||
} else if (state === states.password) {
|
||||
let res = await Api.setPassword(password);
|
||||
if (res.error) {
|
||||
error = res.error;
|
||||
} else {
|
||||
error = undefined;
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.error {
|
||||
color: var(--error);
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: var(--primary);
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
{#if state === states.username}
|
||||
<h3>Enter your Username or your E-Mail Address</h3>
|
||||
<div class="floating group">
|
||||
<input type="text" autocomplete="username" autofocus bind:value={username}>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Username or E-Mail</label>
|
||||
<div class="error" style={!error ? "display: none;" : "" }>{error}</div>
|
||||
</div>
|
||||
{:else}
|
||||
<h3>Enter password for {username}</h3>
|
||||
<div class="floating group">
|
||||
<input type="password" autocomplete="password" autofocus bind:value={password}>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Password</label>
|
||||
<div class="error" style={!error ? "display: none;" : "" }>{error}</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button class="btn" on:click={buttonClick}>
|
||||
Next
|
||||
</button>
|
100
src/Login/Redirect.svelte
Normal file
100
src/Login/Redirect.svelte
Normal file
@ -0,0 +1,100 @@
|
||||
<script>
|
||||
// import {
|
||||
// onMount,
|
||||
// onDestroy
|
||||
// } from "svelte";
|
||||
import {
|
||||
onMount,
|
||||
onDestroy
|
||||
} from 'svelte';
|
||||
|
||||
const basetext = "Logged in. Redirecting";
|
||||
let dots = 0;
|
||||
|
||||
$: text = basetext + ".".repeat(dots);
|
||||
|
||||
let iv;
|
||||
onMount(() => {
|
||||
console.log("Mounted");
|
||||
iv = setInterval(() => {
|
||||
dots++;
|
||||
if (dots > 3)
|
||||
dots = 0;
|
||||
}, 500);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
console.log("on Destroy")
|
||||
clearInterval(iv)
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="scale">
|
||||
<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
|
||||
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
|
||||
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- <div style="text-align: center;"> -->
|
||||
<h3>{text}</h3>
|
||||
<!-- </div> -->
|
||||
|
||||
<style>
|
||||
.checkmark__circle {
|
||||
stroke-dasharray: 166;
|
||||
stroke-dashoffset: 166;
|
||||
stroke-width: 2;
|
||||
stroke-miterlimit: 10;
|
||||
stroke: #7ac142;
|
||||
fill: none;
|
||||
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
stroke-width: 2;
|
||||
stroke: #fff;
|
||||
stroke-miterlimit: 10;
|
||||
margin: 10% auto;
|
||||
box-shadow: inset 0px 0px 0px #7ac142;
|
||||
animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
|
||||
}
|
||||
|
||||
.checkmark__check {
|
||||
transform-origin: 50% 50%;
|
||||
stroke-dasharray: 48;
|
||||
stroke-dashoffset: 48;
|
||||
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
|
||||
}
|
||||
|
||||
@keyframes stroke {
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale3d(1.1, 1.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fill {
|
||||
100% {
|
||||
box-shadow: inset 0px 0px 0px 30px #7ac142;
|
||||
}
|
||||
}
|
||||
|
||||
.scale {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
</style>
|
112
src/Login/Twofactor.svelte
Normal file
112
src/Login/Twofactor.svelte
Normal file
@ -0,0 +1,112 @@
|
||||
<script>
|
||||
import Api, {
|
||||
TFATypes
|
||||
} from "./api";
|
||||
import Icon from "./icons/Icon.svelte";
|
||||
|
||||
import OTCTwoFactor from "./twofactors/otc.svelte";
|
||||
import PushTwoFactor from "./twofactors/push.svelte";
|
||||
import U2FTwoFactor from "./twofactors/u2f.svelte";
|
||||
|
||||
const states = {
|
||||
list: 1,
|
||||
twofactor: 2
|
||||
}
|
||||
|
||||
function getIcon(tf) {
|
||||
switch (tf.type) {
|
||||
case TFATypes.OTC:
|
||||
return "Authenticator"
|
||||
case TFATypes.BACKUP_CODE:
|
||||
return "BackupCode"
|
||||
case TFATypes.U2F:
|
||||
return "SecurityKey"
|
||||
case TFATypes.APP_ALLOW:
|
||||
return "AppPush"
|
||||
}
|
||||
}
|
||||
|
||||
let twofactors = Api.twofactor.map(tf => {
|
||||
return {
|
||||
...tf,
|
||||
icon: getIcon(tf)
|
||||
}
|
||||
});
|
||||
|
||||
let state = states.list;
|
||||
|
||||
let twofactor = undefined;
|
||||
twofactor = twofactors[0];
|
||||
$: console.log(twofactor)
|
||||
|
||||
function onFinish(res) {
|
||||
if (res)
|
||||
finish()
|
||||
else
|
||||
twofactor = undefined;
|
||||
}
|
||||
|
||||
export let finish;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-inline-start: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
border-top: 1px grey solid;
|
||||
padding: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
float: left;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-left: 48px;
|
||||
line-height: 24px;
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
{#if !twofactor}
|
||||
<h3>Select your Authentication method:</h3>
|
||||
<ul>
|
||||
{#each twofactors as tf}
|
||||
<li on:click={()=>twofactor = tf}>
|
||||
<div class="icon">
|
||||
<Icon icon_name={tf.icon}/>
|
||||
</div>
|
||||
|
||||
<div class="name">
|
||||
{tf.name}
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
{#if twofactor.type === TFATypes.OTC}
|
||||
<OTCTwoFactor id={twofactor.id} finish={onFinish} otc={true}/>
|
||||
{:else if twofactor.type === TFATypes.BACKUP_CODE}
|
||||
<OTCTwoFactor id={twofactor.id} finish={onFinish} otc={false}/>
|
||||
{:else if twofactor.type === TFATypes.U2F}
|
||||
<U2FTwoFactor id={twofactor.id} finish={onFinish}/>
|
||||
{:else if twofactor.type === TFATypes.APP_ALLOW}
|
||||
<PushTwoFactor id={twofactor.id} finish={onFinish}/>
|
||||
{:else}
|
||||
<div>Invalid TwoFactor Method!</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div>
|
127
src/Login/api.ts
Normal file
127
src/Login/api.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import request from "../request";
|
||||
import sha from "../sha512";
|
||||
import {
|
||||
setCookie,
|
||||
getCookie
|
||||
} from "../cookie"
|
||||
|
||||
export interface TwoFactor {
|
||||
id: string;
|
||||
name: string;
|
||||
type: TFATypes;
|
||||
}
|
||||
|
||||
export enum TFATypes {
|
||||
OTC,
|
||||
BACKUP_CODE,
|
||||
U2F,
|
||||
APP_ALLOW
|
||||
}
|
||||
|
||||
const Api = {
|
||||
// twofactor: [{
|
||||
// id: "1",
|
||||
// name: "Backup Codes",
|
||||
// type: TFATypes.BACKUP_CODE
|
||||
// }, {
|
||||
// id: "2",
|
||||
// name: "YubiKey",
|
||||
// type: TFATypes.U2F
|
||||
// }, {
|
||||
// id: "3",
|
||||
// name: "Authenticator",
|
||||
// type: TFATypes.OTC
|
||||
// }] as TwoFactor[],
|
||||
getUsername() {
|
||||
return this.username || getCookie("username");
|
||||
},
|
||||
async setUsername(username: string): Promise<{ error: string | undefined }> {
|
||||
return request("/api/user/login", {
|
||||
type: "username",
|
||||
username
|
||||
}, "POST").then(res => {
|
||||
this.salt = res.salt;
|
||||
this.username = username;
|
||||
return {
|
||||
error: undefined
|
||||
}
|
||||
}).catch(err => {
|
||||
let error = err.message;
|
||||
return { error }
|
||||
})
|
||||
},
|
||||
async setPassword(password: string): Promise<{ error: string | undefined, twofactor?: any }> {
|
||||
let pw = sha(this.salt + password);
|
||||
return request("/api/user/login", {
|
||||
type: "password"
|
||||
}, "POST", {
|
||||
username: this.username,
|
||||
password: pw
|
||||
}
|
||||
).then(({
|
||||
login,
|
||||
special,
|
||||
tfa
|
||||
}) => {
|
||||
|
||||
this.login = login;
|
||||
this.special = special;
|
||||
|
||||
if (tfa && Array.isArray(tfa) && tfa.length > 0)
|
||||
this.twofactor = tfa;
|
||||
else
|
||||
this.twofactor = undefined;
|
||||
|
||||
|
||||
return {
|
||||
error: undefined
|
||||
}
|
||||
}).catch(err => {
|
||||
let error = err.message;
|
||||
return { error }
|
||||
})
|
||||
},
|
||||
gettok() {
|
||||
return {
|
||||
login: this.login.token,
|
||||
special: this.special.token
|
||||
}
|
||||
},
|
||||
async sendBackup(id: string, code: string) {
|
||||
return request("/api/user/twofactor/backup", this.gettok(), "PUT", { code, id }).then(({ login_exp, special_exp }) => {
|
||||
this.login.expires = login_exp;
|
||||
this.special.expires = special_exp;
|
||||
return {};
|
||||
}).catch(err => ({ error: err.message }));
|
||||
},
|
||||
async sendOTC(id: string, code: string) {
|
||||
return request("/api/user/twofactor/otc", this.gettok(), "PUT", { code, id }).then(({ login_exp, special_exp }) => {
|
||||
this.login.expires = login_exp;
|
||||
this.special.expires = special_exp;
|
||||
return {};
|
||||
}).catch(error => ({ error: error.message }))
|
||||
},
|
||||
async finish() {
|
||||
let d = new Date()
|
||||
d.setTime(d.getTime() + (30 * 24 * 60 * 60 * 1000)); //Keep the username 30 days
|
||||
setCookie("username", this.username, d.toUTCString());
|
||||
|
||||
setCookie("login", this.login.token, new Date(this.login.expires).toUTCString());
|
||||
setCookie("special", this.special.token, new Date(this.special.expires).toUTCString());
|
||||
|
||||
let url = new URL(window.location.href);
|
||||
let state = url.searchParams.get("state")
|
||||
let red = "/"
|
||||
|
||||
if (state) {
|
||||
let base64 = url.searchParams.get("base64")
|
||||
if (base64)
|
||||
red = atob(state)
|
||||
else
|
||||
red = state
|
||||
}
|
||||
window.location.href = red;
|
||||
}
|
||||
}
|
||||
|
||||
export default Api;
|
1
src/Login/icons/AppPush.svg
Normal file
1
src/Login/icons/AppPush.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><path d="M18.617,1.72c0,-0.949 -0.771,-1.72 -1.721,-1.72l-9.792,0c-0.95,0 -1.721,0.771 -1.721,1.72l0,20.56c0,0.949 0.771,1.72 1.721,1.72l9.792,0c0.95,0 1.721,-0.771 1.721,-1.72l0,-20.56Z" style="fill:#4d4d4d;"/><rect x="6" y="3" width="12" height="18" style="fill:#b3b3b3;"/><path d="M14,1.5c0,-0.129 -0.105,-0.233 -0.233,-0.233l-3.534,0c-0.128,0 -0.233,0.104 -0.233,0.233c0,0.129 0.105,0.233 0.233,0.233l3.534,0c0.128,0 0.233,-0.104 0.233,-0.233Z" style="fill:#b3b3b3;"/><ellipse cx="12" cy="22.5" rx="0.983" ry="1" style="fill:#b3b3b3;"/></svg>
|
After Width: | Height: | Size: 992 B |
1
src/Login/icons/Authenticator.svg
Normal file
1
src/Login/icons/Authenticator.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g><path d="M18.5,12c0,3.59 -2.91,6.5 -6.5,6.5c-3.59,0 -6.5,-2.91 -6.5,-6.5c0,-3.59 2.91,-6.5 6.5,-6.5c1.729,0 3.295,0.679 4.46,1.78l4.169,-3.599c-2.184,-2.265 -5.242,-3.681 -8.629,-3.681c-6.617,0 -12,5.383 -12,12c0,6.617 5.383,12 12,12c6.617,0 12,-5.383 12,-12l-5.5,0Z" style="fill:#999;fill-rule:nonzero;"/><circle id="XMLID_1331_" cx="12" cy="12" r="12" style="fill:#808080;"/><path d="M19,12c0,3.866 -3.134,7 -7,7c-3.866,0 -7,-3.134 -7,-7c0,-3.866 3.134,-7 7,-7c1.88,0 3.583,0.745 4.841,1.951l3.788,-3.27c-2.184,-2.265 -5.242,-3.681 -8.629,-3.681c-6.617,0 -12,5.383 -12,12c0,6.617 5.383,12 12,12c6.617,0 12,-5.383 12,-12l-5,0Z" style="fill:#999;fill-rule:nonzero;"/><circle cx="12" cy="2.5" r="1" style="fill:#b3b3b3;"/><circle cx="12" cy="21.5" r="1" style="fill:#b3b3b3;"/><circle cx="2.5" cy="12" r="1" style="fill:#b3b3b3;"/><path d="M4.575,18.01c0.391,-0.39 1.024,-0.39 1.415,0c0.39,0.391 0.39,1.024 0,1.415c-0.391,0.39 -1.024,0.39 -1.415,0c-0.39,-0.391 -0.39,-1.024 0,-1.415Z" style="fill:#b3b3b3;"/><path d="M18.01,18.01c0.391,-0.39 1.024,-0.39 1.415,0c0.39,0.391 0.39,1.024 0,1.415c-0.391,0.39 -1.024,0.39 -1.415,0c-0.39,-0.391 -0.39,-1.024 0,-1.415Z" style="fill:#b3b3b3;"/><path d="M4.575,4.575c0.391,-0.39 1.024,-0.39 1.415,0c0.39,0.391 0.39,1.024 0,1.415c-0.391,0.39 -1.024,0.39 -1.415,0c-0.39,-0.391 -0.39,-1.024 0,-1.415Z" style="fill:#b3b3b3;"/><circle id="XMLID_1329_" cx="12" cy="12" r="6" style="fill:#808080;"/><circle id="XMLID_1330_" cx="12" cy="12" r="7" style="fill:#808080;"/><path d="M19,12.25c0,-0.042 -0.006,-0.083 -0.006,-0.125c-0.068,3.808 -3.17,6.875 -6.994,6.875c-3.824,0 -6.933,-3.067 -7,-6.875c-0.001,0.042 0,0.083 0,0.125c0,3.866 3.134,7 7,7c3.866,0 7,-3.134 7,-7Z" style="fill:#fff;fill-opacity:0.2;fill-rule:nonzero;"/><path d="M18.92,13l-3.061,0c0.083,-0.321 0.141,-0.653 0.141,-1c0,-2.209 -1.791,-4 -4,-4c-2.209,0 -4,1.791 -4,4c0,1.105 0.448,2.105 1.172,2.828c1.014,1.015 4.057,4.058 4.057,4.058c2.955,-0.525 5.263,-2.899 5.691,-5.886Z" style="fill:#4d4d4d;fill-rule:nonzero;"/><path d="M22,13l-10,0c-0.553,0 -1,-0.448 -1,-1c0,-0.552 0.447,-1 1,-1l10,0c0.553,0 1,0.448 1,1c0,0.552 -0.447,1 -1,1Z" style="fill:#b3b3b3;fill-rule:nonzero;"/><path d="M11.948,11.25l10.104,0c0.409,0 0.776,0.247 0.935,0.592c-0.08,-0.471 -0.492,-0.842 -0.987,-0.842l-10,0c-0.495,0 -0.9,0.33 -0.98,0.801c0.159,-0.345 0.519,-0.551 0.928,-0.551Z" style="fill:#fff;fill-opacity:0.2;fill-rule:nonzero;"/><path d="M23,12c0,0.552 -0.447,1 -1,1l-3.08,0c-0.428,2.988 -2.737,5.362 -5.693,5.886l3.935,3.946c4.04,-1.931 6.838,-6.056 6.838,-10.832l-1,0Z" style="fill:#666;fill-opacity:0.5;fill-rule:nonzero;"/><path d="M12,5c-3.866,0 -7,3.134 -7,7c0,0.042 -0.001,0.069 0,0.111c0.067,-3.808 3.176,-6.861 7,-6.861c2.828,0 4.841,1.701 4.841,1.701c-1.257,-1.198 -2.968,-1.951 -4.841,-1.951Z" style="fill-opacity:0.1;fill-rule:nonzero;"/><circle id="XMLID_4_" cx="12" cy="12" r="12" style="fill:url(#_Linear1);"/></g><defs><linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(21.7566,10.1453,-10.1453,21.7566,1.12171,6.92737)"><stop offset="0" style="stop-color:#fff;stop-opacity:0.2"/><stop offset="1" style="stop-color:#fff;stop-opacity:0"/></linearGradient></defs></svg>
|
After Width: | Height: | Size: 3.6 KiB |
1
src/Login/icons/BackupCode.svg
Normal file
1
src/Login/icons/BackupCode.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M20.562,9.105c0,-0.853 -0.692,-1.544 -1.544,-1.544l-14.036,0c-0.852,0 -1.544,0.691 -1.544,1.544l0,12.351c0,0.852 0.692,1.544 1.544,1.544l14.036,0c0.852,0 1.544,-0.692 1.544,-1.544l0,-12.351Z" style="fill:none;stroke:#000;stroke-width:1.5px;"/><circle cx="12" cy="15.3" r="1.5"/><path d="M16.646,4.28c0,-1.81 -1.47,-3.28 -3.28,-3.28l-2.732,0c-1.81,0 -3.28,1.47 -3.28,3.28l0,3.281l9.292,0l0,-3.281Z" style="fill:none;stroke:#000;stroke-width:1.5px;"/></svg>
|
After Width: | Height: | Size: 927 B |
15
src/Login/icons/Icon.svelte
Normal file
15
src/Login/icons/Icon.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
export let icon_name;
|
||||
</script>
|
||||
|
||||
{#if icon_name === "SecurityKey"}
|
||||
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><path d="M18,7.692c0,-0.925 -0.751,-1.675 -1.675,-1.675l-14.65,0c-0.924,0 -1.675,0.75 -1.675,1.675l0,8.616c0,0.925 0.751,1.675 1.675,1.675l14.65,0c0.924,0 1.675,-0.75 1.675,-1.675l0,-8.616Z" style="fill:#4d4d4d;"/><rect x="18" y="8.011" width="6" height="7.978" style="fill:#4d4d4d;"/><rect x="18" y="10.644" width="4.8" height="1.231" style="fill:#b3b3b3;"/><rect x="18" y="12.229" width="4.8" height="1.164" style="fill:#b3b3b3;"/><rect x="18" y="9.008" width="5.25" height="1.231" style="fill:#b3b3b3;"/><rect x="18" y="13.794" width="5.25" height="1.197" style="fill:#b3b3b3;"/></svg>
|
||||
{:else if icon_name === "Authenticator"}
|
||||
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g><path d="M18.5,12c0,3.59 -2.91,6.5 -6.5,6.5c-3.59,0 -6.5,-2.91 -6.5,-6.5c0,-3.59 2.91,-6.5 6.5,-6.5c1.729,0 3.295,0.679 4.46,1.78l4.169,-3.599c-2.184,-2.265 -5.242,-3.681 -8.629,-3.681c-6.617,0 -12,5.383 -12,12c0,6.617 5.383,12 12,12c6.617,0 12,-5.383 12,-12l-5.5,0Z" style="fill:#999;fill-rule:nonzero;"/><circle id="XMLID_1331_" cx="12" cy="12" r="12" style="fill:#808080;"/><path d="M19,12c0,3.866 -3.134,7 -7,7c-3.866,0 -7,-3.134 -7,-7c0,-3.866 3.134,-7 7,-7c1.88,0 3.583,0.745 4.841,1.951l3.788,-3.27c-2.184,-2.265 -5.242,-3.681 -8.629,-3.681c-6.617,0 -12,5.383 -12,12c0,6.617 5.383,12 12,12c6.617,0 12,-5.383 12,-12l-5,0Z" style="fill:#999;fill-rule:nonzero;"/><circle cx="12" cy="2.5" r="1" style="fill:#b3b3b3;"/><circle cx="12" cy="21.5" r="1" style="fill:#b3b3b3;"/><circle cx="2.5" cy="12" r="1" style="fill:#b3b3b3;"/><path d="M4.575,18.01c0.391,-0.39 1.024,-0.39 1.415,0c0.39,0.391 0.39,1.024 0,1.415c-0.391,0.39 -1.024,0.39 -1.415,0c-0.39,-0.391 -0.39,-1.024 0,-1.415Z" style="fill:#b3b3b3;"/><path d="M18.01,18.01c0.391,-0.39 1.024,-0.39 1.415,0c0.39,0.391 0.39,1.024 0,1.415c-0.391,0.39 -1.024,0.39 -1.415,0c-0.39,-0.391 -0.39,-1.024 0,-1.415Z" style="fill:#b3b3b3;"/><path d="M4.575,4.575c0.391,-0.39 1.024,-0.39 1.415,0c0.39,0.391 0.39,1.024 0,1.415c-0.391,0.39 -1.024,0.39 -1.415,0c-0.39,-0.391 -0.39,-1.024 0,-1.415Z" style="fill:#b3b3b3;"/><circle id="XMLID_1329_" cx="12" cy="12" r="6" style="fill:#808080;"/><circle id="XMLID_1330_" cx="12" cy="12" r="7" style="fill:#808080;"/><path d="M19,12.25c0,-0.042 -0.006,-0.083 -0.006,-0.125c-0.068,3.808 -3.17,6.875 -6.994,6.875c-3.824,0 -6.933,-3.067 -7,-6.875c-0.001,0.042 0,0.083 0,0.125c0,3.866 3.134,7 7,7c3.866,0 7,-3.134 7,-7Z" style="fill:#fff;fill-opacity:0.2;fill-rule:nonzero;"/><path d="M18.92,13l-3.061,0c0.083,-0.321 0.141,-0.653 0.141,-1c0,-2.209 -1.791,-4 -4,-4c-2.209,0 -4,1.791 -4,4c0,1.105 0.448,2.105 1.172,2.828c1.014,1.015 4.057,4.058 4.057,4.058c2.955,-0.525 5.263,-2.899 5.691,-5.886Z" style="fill:#4d4d4d;fill-rule:nonzero;"/><path d="M22,13l-10,0c-0.553,0 -1,-0.448 -1,-1c0,-0.552 0.447,-1 1,-1l10,0c0.553,0 1,0.448 1,1c0,0.552 -0.447,1 -1,1Z" style="fill:#b3b3b3;fill-rule:nonzero;"/><path d="M11.948,11.25l10.104,0c0.409,0 0.776,0.247 0.935,0.592c-0.08,-0.471 -0.492,-0.842 -0.987,-0.842l-10,0c-0.495,0 -0.9,0.33 -0.98,0.801c0.159,-0.345 0.519,-0.551 0.928,-0.551Z" style="fill:#fff;fill-opacity:0.2;fill-rule:nonzero;"/><path d="M23,12c0,0.552 -0.447,1 -1,1l-3.08,0c-0.428,2.988 -2.737,5.362 -5.693,5.886l3.935,3.946c4.04,-1.931 6.838,-6.056 6.838,-10.832l-1,0Z" style="fill:#666;fill-opacity:0.5;fill-rule:nonzero;"/><path d="M12,5c-3.866,0 -7,3.134 -7,7c0,0.042 -0.001,0.069 0,0.111c0.067,-3.808 3.176,-6.861 7,-6.861c2.828,0 4.841,1.701 4.841,1.701c-1.257,-1.198 -2.968,-1.951 -4.841,-1.951Z" style="fill-opacity:0.1;fill-rule:nonzero;"/><circle id="XMLID_4_" cx="12" cy="12" r="12" style="fill:url(#_Linear1);"/></g><defs><linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(21.7566,10.1453,-10.1453,21.7566,1.12171,6.92737)"><stop offset="0" style="stop-color:#fff;stop-opacity:0.2"/><stop offset="1" style="stop-color:#fff;stop-opacity:0"/></linearGradient></defs></svg>
|
||||
{:else if icon_name === "BackupCode"}
|
||||
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M20.562,9.105c0,-0.853 -0.692,-1.544 -1.544,-1.544l-14.036,0c-0.852,0 -1.544,0.691 -1.544,1.544l0,12.351c0,0.852 0.692,1.544 1.544,1.544l14.036,0c0.852,0 1.544,-0.692 1.544,-1.544l0,-12.351Z" style="fill:none;stroke:#000;stroke-width:1.5px;"/><circle cx="12" cy="15.3" r="1.5"/><path d="M16.646,4.28c0,-1.81 -1.47,-3.28 -3.28,-3.28l-2.732,0c-1.81,0 -3.28,1.47 -3.28,3.28l0,3.281l9.292,0l0,-3.281Z" style="fill:none;stroke:#000;stroke-width:1.5px;"/></svg>
|
||||
{:else if icon_name === "AppPush"}
|
||||
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><path d="M18.617,1.72c0,-0.949 -0.771,-1.72 -1.721,-1.72l-9.792,0c-0.95,0 -1.721,0.771 -1.721,1.72l0,20.56c0,0.949 0.771,1.72 1.721,1.72l9.792,0c0.95,0 1.721,-0.771 1.721,-1.72l0,-20.56Z" style="fill:#4d4d4d;"/><rect x="6" y="3" width="12" height="18" style="fill:#b3b3b3;"/><path d="M14,1.5c0,-0.129 -0.105,-0.233 -0.233,-0.233l-3.534,0c-0.128,0 -0.233,0.104 -0.233,0.233c0,0.129 0.105,0.233 0.233,0.233l3.534,0c0.128,0 0.233,-0.104 0.233,-0.233Z" style="fill:#b3b3b3;"/><ellipse cx="12" cy="22.5" rx="0.983" ry="1" style="fill:#b3b3b3;"/></svg>
|
||||
{:else}
|
||||
ERR
|
||||
{/if}
|
1
src/Login/icons/SecurityKey.svg
Normal file
1
src/Login/icons/SecurityKey.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><path d="M18,7.692c0,-0.925 -0.751,-1.675 -1.675,-1.675l-14.65,0c-0.924,0 -1.675,0.75 -1.675,1.675l0,8.616c0,0.925 0.751,1.675 1.675,1.675l14.65,0c0.924,0 1.675,-0.75 1.675,-1.675l0,-8.616Z" style="fill:#4d4d4d;"/><rect x="18" y="8.011" width="6" height="7.978" style="fill:#4d4d4d;"/><rect x="18" y="10.644" width="4.8" height="1.231" style="fill:#b3b3b3;"/><rect x="18" y="12.229" width="4.8" height="1.164" style="fill:#b3b3b3;"/><rect x="18" y="9.008" width="5.25" height="1.231" style="fill:#b3b3b3;"/><rect x="18" y="13.794" width="5.25" height="1.197" style="fill:#b3b3b3;"/></svg>
|
After Width: | Height: | Size: 1.0 KiB |
7
src/Login/main.js
Normal file
7
src/Login/main.js
Normal file
@ -0,0 +1,7 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
var app = new App({
|
||||
target: document.getElementById("content")
|
||||
});
|
||||
|
||||
export default app;
|
35
src/Login/twofactors/codeInput.svelte
Normal file
35
src/Login/twofactors/codeInput.svelte
Normal file
@ -0,0 +1,35 @@
|
||||
<script>
|
||||
import Cleave from "../../cleave"
|
||||
import {
|
||||
onMount
|
||||
} from 'svelte';
|
||||
|
||||
export let error;
|
||||
export let label;
|
||||
export let value;
|
||||
export let length = 6;
|
||||
|
||||
let input;
|
||||
onMount(() => {
|
||||
const cleaveCustom = new Cleave(input, {
|
||||
blocks: [length / 2, length / 2],
|
||||
delimiter: ' ',
|
||||
numericOnly: true
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.error {
|
||||
color: var(--error);
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="floating group">
|
||||
<input bind:this={input} autofocus bind:value={value}>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Code</label>
|
||||
<div class="error" style={!error ? "display: none;" : "" }>{error}</div>
|
||||
</div>
|
56
src/Login/twofactors/otc.svelte
Normal file
56
src/Login/twofactors/otc.svelte
Normal file
@ -0,0 +1,56 @@
|
||||
<script>
|
||||
import ToList from "./toList.svelte";
|
||||
import Api from "../api";
|
||||
import CodeInput from "./codeInput.svelte"
|
||||
|
||||
let error = "";
|
||||
let code = "";
|
||||
export let finish;
|
||||
export let id;
|
||||
|
||||
export let otc = false;
|
||||
let title = otc ? "One Time Code (OTC)" : "Backup Code"
|
||||
let length = otc ? 6 : 8;
|
||||
|
||||
async function sendCode() {
|
||||
let c = code.replace(/\s+/g, "");
|
||||
if (c.length < length) {
|
||||
error = `Code must be ${length} digits long!`;
|
||||
} else {
|
||||
error = "";
|
||||
let res;
|
||||
if (otc)
|
||||
res = await Api.sendOTC(id, c);
|
||||
else
|
||||
res = await Api.sendBackup(id, c);
|
||||
if (res.error)
|
||||
error = res.error;
|
||||
else
|
||||
finish(true)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: var(--primary);
|
||||
margin: 0;
|
||||
margin-left: auto;
|
||||
min-width: 80px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h3>{title}</h3>
|
||||
|
||||
<CodeInput bind:value={code} label="Code" error={error} length={length} />
|
||||
|
||||
<div class="actions">
|
||||
<ToList {finish} />
|
||||
<button class="btn" style="margin-left: auto" on:click={sendCode}>Send</button>
|
||||
</div>
|
393
src/Login/twofactors/push.svelte
Normal file
393
src/Login/twofactors/push.svelte
Normal file
@ -0,0 +1,393 @@
|
||||
<script>
|
||||
import ToList from "./toList.svelte";
|
||||
|
||||
let error = "";
|
||||
let code = "";
|
||||
export let device = "Handy01";
|
||||
export let deviceId = "";
|
||||
|
||||
export let finish;
|
||||
|
||||
async function requestPush() {
|
||||
|
||||
// Push Request
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.windows8 {
|
||||
position: relative;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.windows8 .wBall {
|
||||
position: absolute;
|
||||
width: 53px;
|
||||
height: 53px;
|
||||
opacity: 0;
|
||||
transform: rotate(225deg);
|
||||
-o-transform: rotate(225deg);
|
||||
-ms-transform: rotate(225deg);
|
||||
-webkit-transform: rotate(225deg);
|
||||
-moz-transform: rotate(225deg);
|
||||
animation: orbit 5.7425s infinite;
|
||||
-o-animation: orbit 5.7425s infinite;
|
||||
-ms-animation: orbit 5.7425s infinite;
|
||||
-webkit-animation: orbit 5.7425s infinite;
|
||||
-moz-animation: orbit 5.7425s infinite;
|
||||
}
|
||||
|
||||
.windows8 .wBall .wInnerBall {
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background: rgb(0, 140, 255);
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
.windows8 #wBall_1 {
|
||||
animation-delay: 1.256s;
|
||||
-o-animation-delay: 1.256s;
|
||||
-ms-animation-delay: 1.256s;
|
||||
-webkit-animation-delay: 1.256s;
|
||||
-moz-animation-delay: 1.256s;
|
||||
}
|
||||
|
||||
.windows8 #wBall_2 {
|
||||
animation-delay: 0.243s;
|
||||
-o-animation-delay: 0.243s;
|
||||
-ms-animation-delay: 0.243s;
|
||||
-webkit-animation-delay: 0.243s;
|
||||
-moz-animation-delay: 0.243s;
|
||||
}
|
||||
|
||||
.windows8 #wBall_3 {
|
||||
animation-delay: 0.5065s;
|
||||
-o-animation-delay: 0.5065s;
|
||||
-ms-animation-delay: 0.5065s;
|
||||
-webkit-animation-delay: 0.5065s;
|
||||
-moz-animation-delay: 0.5065s;
|
||||
}
|
||||
|
||||
.windows8 #wBall_4 {
|
||||
animation-delay: 0.7495s;
|
||||
-o-animation-delay: 0.7495s;
|
||||
-ms-animation-delay: 0.7495s;
|
||||
-webkit-animation-delay: 0.7495s;
|
||||
-moz-animation-delay: 0.7495s;
|
||||
}
|
||||
|
||||
.windows8 #wBall_5 {
|
||||
animation-delay: 1.003s;
|
||||
-o-animation-delay: 1.003s;
|
||||
-ms-animation-delay: 1.003s;
|
||||
-webkit-animation-delay: 1.003s;
|
||||
-moz-animation-delay: 1.003s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@keyframes orbit {
|
||||
0% {
|
||||
opacity: 1;
|
||||
z-index: 99;
|
||||
transform: rotate(180deg);
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
7% {
|
||||
opacity: 1;
|
||||
transform: rotate(300deg);
|
||||
animation-timing-function: linear;
|
||||
origin: 0%;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
transform: rotate(410deg);
|
||||
animation-timing-function: ease-in-out;
|
||||
origin: 7%;
|
||||
}
|
||||
|
||||
39% {
|
||||
opacity: 1;
|
||||
transform: rotate(645deg);
|
||||
animation-timing-function: linear;
|
||||
origin: 30%;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 1;
|
||||
transform: rotate(770deg);
|
||||
animation-timing-function: ease-out;
|
||||
origin: 39%;
|
||||
}
|
||||
|
||||
75% {
|
||||
opacity: 1;
|
||||
transform: rotate(900deg);
|
||||
animation-timing-function: ease-out;
|
||||
origin: 70%;
|
||||
}
|
||||
|
||||
76% {
|
||||
opacity: 0;
|
||||
transform: rotate(900deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: rotate(900deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes orbit {
|
||||
0% {
|
||||
opacity: 1;
|
||||
z-index: 99;
|
||||
-o-transform: rotate(180deg);
|
||||
-o-animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
7% {
|
||||
opacity: 1;
|
||||
-o-transform: rotate(300deg);
|
||||
-o-animation-timing-function: linear;
|
||||
-o-origin: 0%;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
-o-transform: rotate(410deg);
|
||||
-o-animation-timing-function: ease-in-out;
|
||||
-o-origin: 7%;
|
||||
}
|
||||
|
||||
39% {
|
||||
opacity: 1;
|
||||
-o-transform: rotate(645deg);
|
||||
-o-animation-timing-function: linear;
|
||||
-o-origin: 30%;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 1;
|
||||
-o-transform: rotate(770deg);
|
||||
-o-animation-timing-function: ease-out;
|
||||
-o-origin: 39%;
|
||||
}
|
||||
|
||||
75% {
|
||||
opacity: 1;
|
||||
-o-transform: rotate(900deg);
|
||||
-o-animation-timing-function: ease-out;
|
||||
-o-origin: 70%;
|
||||
}
|
||||
|
||||
76% {
|
||||
opacity: 0;
|
||||
-o-transform: rotate(900deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-o-transform: rotate(900deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-ms-keyframes orbit {
|
||||
0% {
|
||||
opacity: 1;
|
||||
z-index: 99;
|
||||
-ms-transform: rotate(180deg);
|
||||
-ms-animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
7% {
|
||||
opacity: 1;
|
||||
-ms-transform: rotate(300deg);
|
||||
-ms-animation-timing-function: linear;
|
||||
-ms-origin: 0%;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
-ms-transform: rotate(410deg);
|
||||
-ms-animation-timing-function: ease-in-out;
|
||||
-ms-origin: 7%;
|
||||
}
|
||||
|
||||
39% {
|
||||
opacity: 1;
|
||||
-ms-transform: rotate(645deg);
|
||||
-ms-animation-timing-function: linear;
|
||||
-ms-origin: 30%;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 1;
|
||||
-ms-transform: rotate(770deg);
|
||||
-ms-animation-timing-function: ease-out;
|
||||
-ms-origin: 39%;
|
||||
}
|
||||
|
||||
75% {
|
||||
opacity: 1;
|
||||
-ms-transform: rotate(900deg);
|
||||
-ms-animation-timing-function: ease-out;
|
||||
-ms-origin: 70%;
|
||||
}
|
||||
|
||||
76% {
|
||||
opacity: 0;
|
||||
-ms-transform: rotate(900deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-ms-transform: rotate(900deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes orbit {
|
||||
0% {
|
||||
opacity: 1;
|
||||
z-index: 99;
|
||||
-webkit-transform: rotate(180deg);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
7% {
|
||||
opacity: 1;
|
||||
-webkit-transform: rotate(300deg);
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-origin: 0%;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
-webkit-transform: rotate(410deg);
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
-webkit-origin: 7%;
|
||||
}
|
||||
|
||||
39% {
|
||||
opacity: 1;
|
||||
-webkit-transform: rotate(645deg);
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-origin: 30%;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 1;
|
||||
-webkit-transform: rotate(770deg);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
-webkit-origin: 39%;
|
||||
}
|
||||
|
||||
75% {
|
||||
opacity: 1;
|
||||
-webkit-transform: rotate(900deg);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
-webkit-origin: 70%;
|
||||
}
|
||||
|
||||
76% {
|
||||
opacity: 0;
|
||||
-webkit-transform: rotate(900deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: rotate(900deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes orbit {
|
||||
0% {
|
||||
opacity: 1;
|
||||
z-index: 99;
|
||||
-moz-transform: rotate(180deg);
|
||||
-moz-animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
7% {
|
||||
opacity: 1;
|
||||
-moz-transform: rotate(300deg);
|
||||
-moz-animation-timing-function: linear;
|
||||
-moz-origin: 0%;
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
-moz-transform: rotate(410deg);
|
||||
-moz-animation-timing-function: ease-in-out;
|
||||
-moz-origin: 7%;
|
||||
}
|
||||
|
||||
39% {
|
||||
opacity: 1;
|
||||
-moz-transform: rotate(645deg);
|
||||
-moz-animation-timing-function: linear;
|
||||
-moz-origin: 30%;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 1;
|
||||
-moz-transform: rotate(770deg);
|
||||
-moz-animation-timing-function: ease-out;
|
||||
-moz-origin: 39%;
|
||||
}
|
||||
|
||||
75% {
|
||||
opacity: 1;
|
||||
-moz-transform: rotate(900deg);
|
||||
-moz-animation-timing-function: ease-out;
|
||||
-moz-origin: 70%;
|
||||
}
|
||||
|
||||
76% {
|
||||
opacity: 0;
|
||||
-moz-transform: rotate(900deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
-moz-transform: rotate(900deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<h3>SMS</h3>
|
||||
|
||||
<p>A code was sent to your Device <b>{device}</b></p>
|
||||
|
||||
<div class="windows8">
|
||||
<div class="wBall" id="wBall_1">
|
||||
<div class="wInnerBall"></div>
|
||||
</div>
|
||||
<div class="wBall" id="wBall_2">
|
||||
<div class="wInnerBall"></div>
|
||||
</div>
|
||||
<div class="wBall" id="wBall_3">
|
||||
<div class="wInnerBall"></div>
|
||||
</div>
|
||||
<div class="wBall" id="wBall_4">
|
||||
<div class="wInnerBall"></div>
|
||||
</div>
|
||||
<div class="wBall" id="wBall_5">
|
||||
<div class="wInnerBall"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error">{error}</div>
|
||||
<ToList {finish} />
|
49
src/Login/twofactors/sms.svelte
Normal file
49
src/Login/twofactors/sms.svelte
Normal file
@ -0,0 +1,49 @@
|
||||
<script>
|
||||
import ToList from "./toList.svelte";
|
||||
|
||||
const states = {
|
||||
approve: 1,
|
||||
enter: 2
|
||||
}
|
||||
let state = states.approve;
|
||||
|
||||
let error = "";
|
||||
let code = "";
|
||||
export let number = "+4915...320";
|
||||
//export let finish;
|
||||
|
||||
function validateCode() {
|
||||
|
||||
}
|
||||
|
||||
function sendCode() {
|
||||
// Send request to Server
|
||||
state = states.enter;
|
||||
//finish()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--error: red;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
</style>
|
||||
|
||||
<h3>SMS</h3>
|
||||
{#if state === states.approve}
|
||||
<p>Send SMS to {number}</p>
|
||||
<button class="btn" on:click={sendCode}>Send</button>
|
||||
{:else}
|
||||
<p>A code was sent to you. Please enter</p>
|
||||
<input type="number" placeholder="Code" bind:value={code} />
|
||||
<button on:click={validateCode}>Send</button><br>
|
||||
<a href="# " on:click|preventDefault={() => state = states.approve}>Not received?</a>
|
||||
{/if}
|
||||
<div class="error">{error}</div>
|
||||
|
||||
<ToList {finish}/>
|
13
src/Login/twofactors/toList.svelte
Normal file
13
src/Login/twofactors/toList.svelte
Normal file
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
export let finish = () => {};
|
||||
</script>
|
||||
<style>
|
||||
a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>
|
||||
<a href="# " on:click={evt=>evt.preventDefault() || finish(false)}>Choose another Method</a>
|
||||
</p>
|
72
src/Login/twofactors/u2f.svelte
Normal file
72
src/Login/twofactors/u2f.svelte
Normal file
@ -0,0 +1,72 @@
|
||||
<script>
|
||||
import ToList from "./toList.svelte";
|
||||
|
||||
export let finish;
|
||||
|
||||
const states = {
|
||||
getChallenge: 0,
|
||||
requestUser: 1,
|
||||
sendChallenge: 2,
|
||||
error: 3
|
||||
}
|
||||
|
||||
let state = states.getChallenge;
|
||||
|
||||
let error = "";
|
||||
|
||||
const onError = err => {
|
||||
state = states.error;
|
||||
error = err.message;
|
||||
};
|
||||
|
||||
let challenge;
|
||||
|
||||
async function requestUser() {
|
||||
state = states.requestUser;
|
||||
let res = await window.navigator.credentials.get({
|
||||
publicKey: challenge
|
||||
})
|
||||
state = states.sendChallenge();
|
||||
let r = res.response;
|
||||
let data = encode({
|
||||
authenticatorData: r.authenticatorData,
|
||||
clientDataJSON: r.clientDataJSON,
|
||||
signature: r.signature,
|
||||
userHandle: r.userHandle
|
||||
});
|
||||
let {
|
||||
success
|
||||
} = fetch("https://localhost:8444/auth", {
|
||||
body: data,
|
||||
method: "POST"
|
||||
}).then(res => res.json())
|
||||
if (success) {
|
||||
finish(true);
|
||||
}
|
||||
}
|
||||
|
||||
async function getChallenge() {
|
||||
state = states.getChallenge;
|
||||
challenge = await fetch("https://localhost:8444/auth")
|
||||
.then(res => res.arrayBuffer())
|
||||
.then(data => decode(MessagePack.Buffer.from(data)));
|
||||
|
||||
requestUser().catch(onError);
|
||||
}
|
||||
getChallenge().catch(onError)
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--error: red;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
</style>
|
||||
|
||||
<h3>U2F Security Key</h3>
|
||||
<h4>This Method is currently not supported. Please choose another one!</h4>
|
||||
<ToList {finish} />
|
245
src/Public/global.css
Normal file
245
src/Public/global.css
Normal file
@ -0,0 +1,245 @@
|
||||
:root {
|
||||
--primary: #1E88E5;
|
||||
--mdc-theme-primary: var(--primary);
|
||||
--mdc-theme-primary-bg: var(--mdc-theme--primary);
|
||||
--mdc-theme-on-primary: white;
|
||||
--error: #ff2f00;
|
||||
--border-color: #ababab;
|
||||
|
||||
--default-font-size: 1.05rem;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: 'Roboto', 'Helvetica', sans-serif;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
color: #636363;
|
||||
position: relative;
|
||||
background: #eee;
|
||||
height: 100%;
|
||||
font-size: var(--default-font-size);
|
||||
}
|
||||
|
||||
.group {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
.floating>input {
|
||||
font-size: 1.2rem;
|
||||
padding: 10px 10px 10px 5px;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
display: block;
|
||||
background: #fafafa;
|
||||
background: unset;
|
||||
color: #636363;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
/* border-bottom: 1px solid #757575; */
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.floating>input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Label */
|
||||
|
||||
.floating>label {
|
||||
color: #999;
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 5px;
|
||||
top: 10px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* active */
|
||||
|
||||
.floating>input:focus~label,
|
||||
.floating>input.used~label {
|
||||
top: -.75em;
|
||||
transform: scale(.75);
|
||||
left: -2px;
|
||||
/* font-size: 14px; */
|
||||
color: var(--primary);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
/* Underline */
|
||||
|
||||
.bar {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bar:before,
|
||||
.bar:after {
|
||||
content: '';
|
||||
height: 2px;
|
||||
width: 0;
|
||||
bottom: 1px;
|
||||
position: absolute;
|
||||
background: var(--primary);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.bar:before {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.bar:after {
|
||||
right: 50%;
|
||||
}
|
||||
|
||||
/* active */
|
||||
|
||||
.floating>input:focus~.bar:before,
|
||||
.floating>input:focus~.bar:after {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* Highlight */
|
||||
|
||||
.highlight {
|
||||
position: absolute;
|
||||
height: 60%;
|
||||
width: 100px;
|
||||
top: 25%;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* active */
|
||||
|
||||
.floating>input:focus~.highlight {
|
||||
animation: inputHighlighter 0.3s ease;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
@keyframes inputHighlighter {
|
||||
from {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
to {
|
||||
width: 0;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
position: relative;
|
||||
|
||||
display: block;
|
||||
margin: 2rem;
|
||||
padding: 0 1em;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
border-width: 0;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .6);
|
||||
|
||||
background-color: #cccccc;
|
||||
color: #ecf0f1;
|
||||
|
||||
transition: background-color .3s;
|
||||
|
||||
height: 48px;
|
||||
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.btn:hover,
|
||||
.btn:focus {
|
||||
filter: brightness(90%);
|
||||
}
|
||||
|
||||
.btn>* {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn span {
|
||||
display: block;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.btn:before {
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
||||
display: block;
|
||||
width: 0;
|
||||
padding-top: 0;
|
||||
|
||||
border-radius: 100%;
|
||||
|
||||
background-color: rgba(236, 240, 241, .3);
|
||||
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
-moz-transform: translate(-50%, -50%);
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
-o-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.btn:active:before {
|
||||
width: 120%;
|
||||
padding-top: 120%;
|
||||
|
||||
transition: width .2s ease-out, padding-top .2s ease-out;
|
||||
}
|
||||
|
||||
.loader_box {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.loader:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin: 1px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid var(--primary);
|
||||
border-color: var(--primary) transparent var(--primary) transparent;
|
||||
animation: loader 1.2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
36
src/Public/main.js
Normal file
36
src/Public/main.js
Normal file
@ -0,0 +1,36 @@
|
||||
// import "./global.css";
|
||||
(() => {
|
||||
const elements = new WeakSet();
|
||||
|
||||
function check() {
|
||||
document.querySelectorAll(".floating>input").forEach(e => {
|
||||
if (elements.has(e)) return;
|
||||
elements.add(e);
|
||||
|
||||
function checkState() {
|
||||
console.log("Check State");
|
||||
if (e.value !== "") {
|
||||
if (e.classList.contains("used")) return;
|
||||
e.classList.add("used")
|
||||
} else {
|
||||
if (e.classList.contains("used")) e.classList.remove("used")
|
||||
}
|
||||
}
|
||||
|
||||
e.addEventListener("change", () => checkState())
|
||||
checkState()
|
||||
})
|
||||
};
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
check();
|
||||
});
|
||||
|
||||
|
||||
// Start observing the target node for configured mutations
|
||||
observer.observe(window.document, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
check();
|
||||
})()
|
195
src/User/App.svelte
Normal file
195
src/User/App.svelte
Normal file
@ -0,0 +1,195 @@
|
||||
<script>
|
||||
import AccountPage from "./Pages/Account.svelte";
|
||||
import SecurityPage from "./Pages/Security.svelte";
|
||||
import {
|
||||
slide,
|
||||
fade
|
||||
} from 'svelte/transition';
|
||||
|
||||
const pages = [{
|
||||
id: "account",
|
||||
title: "Account",
|
||||
icon: "",
|
||||
component: AccountPage
|
||||
},
|
||||
{
|
||||
id: "security",
|
||||
title: "Security",
|
||||
icon: "",
|
||||
component: SecurityPage
|
||||
}
|
||||
];
|
||||
|
||||
function getPage() {
|
||||
let pageid = window.location.hash.slice(1);
|
||||
return pages.find(e => e.id === pageid) || pages[0];
|
||||
}
|
||||
|
||||
let page = getPage();
|
||||
window.addEventListener("hashchange", () => {
|
||||
page = getPage();
|
||||
})
|
||||
// $: title = pages.find(e => e.id === page).title;
|
||||
|
||||
|
||||
const mq = window.matchMedia("(min-width: 45rem)");
|
||||
let sidebar_button = !mq.matches;
|
||||
mq.addEventListener("change", (ev) => {
|
||||
sidebar_button = !ev.matches;
|
||||
})
|
||||
|
||||
let sidebar_active = false;
|
||||
|
||||
|
||||
|
||||
function setPage(pageid) {
|
||||
let pg = pages.find(e => e.id === pageid)
|
||||
if (!pg) {
|
||||
throw new Error("Invalid Page " + pageid);
|
||||
} else {
|
||||
let url = new URL(window.location.href);
|
||||
url.hash = pg.id;
|
||||
window.history.pushState({}, pg.title, url);
|
||||
page = getPage();
|
||||
}
|
||||
|
||||
sidebar_active = false;
|
||||
}
|
||||
|
||||
|
||||
let loading = true;
|
||||
|
||||
import NavigationBar from "./NavigationBar.svelte";
|
||||
</script>
|
||||
<div class:loading class="root">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
{#if sidebar_button}
|
||||
<button on:click={()=>sidebar_active = !sidebar_active}>
|
||||
<svg id="Layer_1" style="enable-background:new 0 0 32 32;" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z"/></svg>
|
||||
</button>
|
||||
{/if}
|
||||
<h1>{page.title}</h1>
|
||||
</div>
|
||||
<div class="sidebar" class:sidebar-visible={sidebar_active}>
|
||||
<NavigationBar open={setPage} pages={pages} />
|
||||
</div>
|
||||
<div class="content">
|
||||
<svelte:component this={page.component} bind:loading />
|
||||
</div>
|
||||
<div class="footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<div class="loader_container">
|
||||
<div class="loader_box">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.loading {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--sidebar-width: 250px;
|
||||
}
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
height: 100%;
|
||||
grid-template-columns: auto 100%;
|
||||
grid-template-rows: 60px auto 60px;
|
||||
grid-template-areas:
|
||||
"sidebar header"
|
||||
"sidebar mc"
|
||||
"sidebar footer"
|
||||
}
|
||||
|
||||
.header {
|
||||
grid-area: header;
|
||||
background-color: var(--primary);
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header > h1 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 24px;
|
||||
line-height: 36px;
|
||||
color: white;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.header>button {
|
||||
height: 36px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.header>button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.151)
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
grid-area: sidebar;
|
||||
transition: width .2s;
|
||||
background-color: lightgrey;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar-visible {
|
||||
width: var(--sidebar-width);
|
||||
transition: width .2s;
|
||||
box-shadow: 10px 0px 10px 2px rgba(0, 0, 0, 0.52);
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-area: mc;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
grid-area: footer;
|
||||
}
|
||||
|
||||
@media (min-width: 45rem) {
|
||||
.container {
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
transition: all .2s;
|
||||
box-shadow: 10px 0px 10px 2px rgba(0, 0, 0, 0.52);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.loader_container {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
46
src/User/NavigationBar.svelte
Normal file
46
src/User/NavigationBar.svelte
Normal file
@ -0,0 +1,46 @@
|
||||
<script>
|
||||
export let open;
|
||||
export let pages = []
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--rel-size: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
.container {
|
||||
height: calc(var(--rel-size) * 3);
|
||||
padding: var(--rel-size);
|
||||
display: flex;
|
||||
/* align-content: center; */
|
||||
align-items: center;
|
||||
/* justify-content: center; */
|
||||
}
|
||||
|
||||
.icon {
|
||||
/* float: left; */
|
||||
width: calc(var(--rel-size) * 3);
|
||||
height: calc(var(--rel-size) * 3);
|
||||
}
|
||||
|
||||
.icon>img {
|
||||
width: calc(var(--rel-size) * 3);
|
||||
height: calc(var(--rel-size) * 3);
|
||||
}
|
||||
|
||||
.title {
|
||||
/* margin: auto; */
|
||||
margin-left: var(--rel-size);
|
||||
/* height: 100%; */
|
||||
}
|
||||
</style>
|
||||
|
||||
{#each pages as page}
|
||||
<div class="container" on:click={() => open(page.id)}>
|
||||
<div class="icon"><img src={page.icon} /></div>
|
||||
<h3 class="title">
|
||||
{page.title}
|
||||
</h3>
|
||||
</div>
|
||||
{/each}
|
166
src/User/Pages/Account.svelte
Normal file
166
src/User/Pages/Account.svelte
Normal file
@ -0,0 +1,166 @@
|
||||
<script>
|
||||
import Box from "./Box.svelte";
|
||||
import BoxItem from "./BoxItem.svelte";
|
||||
import NextIcon from "./NextIcon.svelte";
|
||||
|
||||
import request from "../../request"
|
||||
|
||||
export let loading = false;
|
||||
let error = undefined;
|
||||
|
||||
|
||||
const genderMap = new Map();
|
||||
genderMap.set(0, "None");
|
||||
genderMap.set(1, "Male");
|
||||
genderMap.set(2, "Female");
|
||||
genderMap.set(3, "Other");
|
||||
|
||||
let name = "";
|
||||
let gender = 0;
|
||||
$: genderHuman = genderMap.get(gender) || "ERROR";
|
||||
let birthday = undefined;
|
||||
|
||||
async function saveName() {
|
||||
//TODO: implement
|
||||
await load();
|
||||
}
|
||||
|
||||
async function saveGender() {
|
||||
//TODO: implement
|
||||
await load();
|
||||
}
|
||||
|
||||
async function loadProfile() {
|
||||
try {
|
||||
let {
|
||||
user
|
||||
} = await request("/api/user/account", {}, "GET", undefined, true)
|
||||
|
||||
name = user.name;
|
||||
// username = user.username;
|
||||
gender = user.gender;
|
||||
birthday = user.birthday ? new Date(user.birthday).toLocaleDateString() : undefined;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
error = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let email = ["mail@fabianstamm.de", "fabian.stamm.koe@gmail.com"];
|
||||
let phone = ["+1 1233 123123123", "+21 1233 123 123 1"];
|
||||
|
||||
async function loadContact() {
|
||||
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading = true;
|
||||
await Promise.all([
|
||||
loadProfile(),
|
||||
loadContact()
|
||||
])
|
||||
loading = false;
|
||||
}
|
||||
|
||||
load();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
background-color: var(--primary);
|
||||
margin: auto 0;
|
||||
margin-left: 1rem;
|
||||
font-size: 1rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.floating {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-container>*:first-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: unset;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
color: unset;
|
||||
font-size: unset;
|
||||
border-bottom: 1px solid #757575;
|
||||
/* Firefox */
|
||||
-moz-appearance: none;
|
||||
/* Safari and Chrome */
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
select>option {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select-wrapper::after {
|
||||
content: ">";
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1rem;
|
||||
transform: rotate(90deg) scaleY(2);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
</style>
|
||||
|
||||
<Box>
|
||||
<h1>Profile</h1>
|
||||
{#if error}
|
||||
<p class="error">{error}</p>
|
||||
{/if}
|
||||
<BoxItem name="Name" value={name}>
|
||||
<div class="input-container">
|
||||
<div class="floating group">
|
||||
<input type="text" autocomplete="username" bind:value={name}>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Name</label>
|
||||
</div>
|
||||
<button class="btn" on:click={saveName}>Save</button>
|
||||
</div>
|
||||
</BoxItem>
|
||||
<BoxItem name="Gender" value={genderHuman}>
|
||||
<div class="input-container">
|
||||
<div class="select-wrapper">
|
||||
<select bind:value={gender}>
|
||||
<option value={1}>Male</option>
|
||||
<option value={2}>Female</option>
|
||||
<option value={3}>Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn" on:click={saveGender}>Save</button>
|
||||
</div>
|
||||
</BoxItem>
|
||||
<BoxItem name="Birthday" value={birthday} />
|
||||
<BoxItem name="Password" value="******" />
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<h1>Contact</h1>
|
||||
<BoxItem name="E-Mail" value={email} />
|
||||
<BoxItem name="Phone" value={phone} />
|
||||
</Box>
|
36
src/User/Pages/Box.svelte
Normal file
36
src/User/Pages/Box.svelte
Normal file
@ -0,0 +1,36 @@
|
||||
<style>
|
||||
.box {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.30), 0 5px 4px rgba(0, 0, 0, 0.22);
|
||||
padding: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.box> :global(h1) {
|
||||
margin: 0;
|
||||
margin-bottom: 1rem;
|
||||
color: #444444;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.box> :global(div) {
|
||||
padding: 16px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.box> :global(div):first-of-type {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
@media (min-width: 45rem) {
|
||||
.box {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="box">
|
||||
<slot></slot>
|
||||
</div>
|
97
src/User/Pages/BoxItem.svelte
Normal file
97
src/User/Pages/BoxItem.svelte
Normal file
@ -0,0 +1,97 @@
|
||||
<script>
|
||||
import {
|
||||
slide
|
||||
} from 'svelte/transition';
|
||||
import NextIcon from "./NextIcon.svelte"
|
||||
export let name = "";
|
||||
export let value = "";
|
||||
export let open = false;
|
||||
export let highlight = false;
|
||||
|
||||
function toggleOpen(ev) {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.root:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.values {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: calc(100% - var(--default-font-size) - 16px);
|
||||
}
|
||||
|
||||
.values>div:first-child {
|
||||
transform-origin: left;
|
||||
transform: scale(0.95);
|
||||
margin-right: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.values>div:nth-child(2) {
|
||||
color: black;
|
||||
}
|
||||
|
||||
:global(svg) {
|
||||
margin: auto 8px auto 8px;
|
||||
height: var(--default-font-size);
|
||||
min-width: var(--default-font-size);
|
||||
}
|
||||
|
||||
.body {
|
||||
box-sizing: border-box;
|
||||
padding: .1px;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@media (min-width: 45rem) {
|
||||
.values {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.values>div:first-child {
|
||||
transform: unset;
|
||||
flex-basis: 120px;
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
.highlight-element {
|
||||
background-color: #7bff003b;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="root" class:highlight-element={highlight}>
|
||||
<div class="container" on:click={()=>open=!open}>
|
||||
<div class="values">
|
||||
<div>{name}</div>
|
||||
<div>
|
||||
{#if Array.isArray(value)}
|
||||
{#each value as v, i}
|
||||
{v}
|
||||
{#if i < value.length - 1}
|
||||
<br/>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
{value}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<NextIcon rotation={open ? -90 : 90} />
|
||||
</div>
|
||||
{#if open}
|
||||
<div class="body" transition:slide>
|
||||
<slot></slot>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
13
src/User/Pages/NextIcon.svelte
Normal file
13
src/User/Pages/NextIcon.svelte
Normal file
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
export let rotation;
|
||||
</script>
|
||||
|
||||
<svg style={`enable-background:new 0 0 35.414 35.414; transform: rotate(${rotation}deg); transition: all .4s;`}
|
||||
version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 35.414 35.414" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon points="27.051,17 9.905,0 8.417,1.414 24.674,17.707 8.363,34 9.914,35.414 27.051,18.414 " />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
155
src/User/Pages/Security.svelte
Normal file
155
src/User/Pages/Security.svelte
Normal file
@ -0,0 +1,155 @@
|
||||
<script context="module">
|
||||
const TFATypes = new Map()
|
||||
TFATypes.set(0, "Authenticator")
|
||||
TFATypes.set(1, "Backup Codes")
|
||||
TFATypes.set(2, "YubiKey")
|
||||
TFATypes.set(3, "Push Notification")
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import Box from "./Box.svelte";
|
||||
import BoxItem from "./BoxItem.svelte";
|
||||
import NextIcon from "./NextIcon.svelte";
|
||||
import request from "../../request";
|
||||
|
||||
export let loading = false;
|
||||
|
||||
let twofactor = [];
|
||||
|
||||
async function deleteTFA(id) {
|
||||
let res = await request("/api/user/twofactor/" + id, undefined, "DELETE", undefined, true);
|
||||
loadTwoFactor();
|
||||
}
|
||||
|
||||
async function loadTwoFactor() {
|
||||
let res = await request("/api/user/twofactor", undefined, undefined, undefined, true);
|
||||
twofactor = res.methods;
|
||||
}
|
||||
|
||||
|
||||
let token = [];
|
||||
|
||||
async function revoke(id) {
|
||||
let res = await request("/api/user/token/" + id, undefined, "DELETE", undefined, true);
|
||||
loadToken();
|
||||
}
|
||||
|
||||
async function loadToken() {
|
||||
loading = true;
|
||||
let res = await request("/api/user/token", undefined, undefined, undefined, true);
|
||||
token = res.token;
|
||||
loading = false;
|
||||
}
|
||||
|
||||
loadToken();
|
||||
loadTwoFactor();
|
||||
</script>
|
||||
|
||||
|
||||
<Box>
|
||||
<h1>Two Factor</h1>
|
||||
<BoxItem name="Add new" open={false}></BoxItem>
|
||||
{#each twofactor as t}
|
||||
<BoxItem name={TFATypes.get(t.type)} value={t.name} highlight={t.isthis}>
|
||||
<button class="btn" style="background: var(--error)" on:click={()=>deleteTFA(t.id)}>Delete</button>
|
||||
</BoxItem>
|
||||
{/each}
|
||||
<!-- <BoxItem name="Name" value={name} open={false}>
|
||||
<div class="input-container">
|
||||
<div class="floating group">
|
||||
<input type="text" autocomplete="username" bind:value={name}>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Name</label>
|
||||
</div>
|
||||
<button class="btn" on:click={saveName}>Save</button>
|
||||
</div>
|
||||
</BoxItem>
|
||||
<BoxItem name="Gender" value={gender} open={true}>
|
||||
<div class="input-container">
|
||||
<div class="select-wrapper">
|
||||
<select>
|
||||
<option value="1">Male</option>
|
||||
<option value="2">Female</option>
|
||||
<option value="3">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn" on:click={saveName}>Save</button>
|
||||
</div>
|
||||
</BoxItem>
|
||||
<BoxItem name="Birthday" value={birthday} />
|
||||
<BoxItem name="Password" value="******" /> -->
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<h1>Anmeldungen</h1>
|
||||
|
||||
{#each token as t}
|
||||
<BoxItem name={t.browser} value={t.ip} highlight={t.isthis}>
|
||||
<button class="btn" style="background: var(--error)" on:click={()=>revoke(t.id)}>Revoke</button>
|
||||
</BoxItem>
|
||||
{:else}
|
||||
<span>No Tokens</span>
|
||||
{/each}
|
||||
|
||||
<!-- <BoxItem name="E-Mail" value={email} />
|
||||
<BoxItem name="Phone" value={phone} /> -->
|
||||
</Box>
|
||||
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
background-color: var(--primary);
|
||||
margin: auto 0;
|
||||
margin-left: 1rem;
|
||||
font-size: 1rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.floating {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-container>*:first-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: unset;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
color: unset;
|
||||
font-size: unset;
|
||||
border-bottom: 1px solid #757575;
|
||||
/* Firefox */
|
||||
-moz-appearance: none;
|
||||
/* Safari and Chrome */
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
select>option {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select-wrapper::after {
|
||||
content: ">";
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1rem;
|
||||
transform: rotate(90deg) scaleY(2);
|
||||
}
|
||||
</style>
|
7
src/User/main.js
Normal file
7
src/User/main.js
Normal file
@ -0,0 +1,7 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
var app = new App({
|
||||
target: document.getElementById("content")
|
||||
});
|
||||
|
||||
export default app;
|
1515
src/cleave.js
Normal file
1515
src/cleave.js
Normal file
File diff suppressed because it is too large
Load Diff
20
src/cookie.ts
Normal file
20
src/cookie.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export function setCookie(cname: string, cvalue: string, exdate: string) {
|
||||
const expires = exdate ? `;expires=${exdate}` : "";
|
||||
document.cookie = `${cname}=${cvalue}${expires}`
|
||||
}
|
||||
|
||||
export function getCookie(cname: string) {
|
||||
const name = cname + "=";
|
||||
const dc = decodeURIComponent(document.cookie);
|
||||
const ca = dc.split(';');
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
43
src/request.ts
Normal file
43
src/request.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { getCookie } from "./cookie";
|
||||
|
||||
// const baseURL = "https://auth.stamm.me";
|
||||
const baseURL = "http://localhost:3000";
|
||||
|
||||
export default async function request(endpoint: string, parameters: { [key: string]: string } = {}, method: "GET" | "POST" | "DELETE" | "PUT" = "GET", body?: any, authInParam = false) {
|
||||
let pairs = [];
|
||||
|
||||
if (authInParam) {
|
||||
parameters.login = getCookie("login");
|
||||
parameters.special = getCookie("special");
|
||||
}
|
||||
|
||||
for (let key in parameters) {
|
||||
pairs.push(key + "=" + parameters[key]);
|
||||
}
|
||||
|
||||
let url = endpoint;
|
||||
if (pairs.length > 0) {
|
||||
url += "?" + pairs.join("&");
|
||||
}
|
||||
|
||||
return fetch(baseURL + url, {
|
||||
method,
|
||||
body: JSON.stringify(body),
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}).then(e => {
|
||||
if (e.status !== 200) throw new Error(e.statusText)
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
if (data.additional && data.additional.auth) {
|
||||
let state = btoa(window.location.pathname + window.location.hash);
|
||||
// window.location.href = `/login?state=${state}&base64=true`;
|
||||
}
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
return data;
|
||||
})
|
||||
}
|
1
src/sha512.js
Normal file
1
src/sha512.js
Normal file
File diff suppressed because one or more lines are too long
6
src/tsconfig.json
Normal file
6
src/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
// "noImplicitAny": true,
|
||||
// "removeComments": true,
|
||||
// "preserveConstEnums": true,
|
||||
// "sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"build.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user