parent
b15783fe6a
commit
516d2df2e1
56 changed files with 4054 additions and 7238 deletions
@ -1,3 +1,36 @@ |
||||
{ |
||||
"extends": "next/core-web-vitals" |
||||
"extends": "next/core-web-vitals", |
||||
"rules": { |
||||
"brace-style": [ |
||||
"error", |
||||
"1tbs", |
||||
{ "allowSingleLine": true } |
||||
], |
||||
"indent": [ |
||||
"error", |
||||
"tab", |
||||
{ "SwitchCase": 1, "ignoreComments": true, "MemberExpression": 1 } |
||||
], |
||||
"linebreak-style": [ |
||||
"error", |
||||
"unix" |
||||
], |
||||
"jsx-quotes": [ |
||||
"error", |
||||
"prefer-single" |
||||
], |
||||
"quotes": [ |
||||
"error", |
||||
"single" |
||||
], |
||||
"semi": [ |
||||
"error", |
||||
"always" |
||||
], |
||||
"curly": ["error"], |
||||
"no-multiple-empty-lines": ["error", { "max": 1 }], |
||||
"no-template-curly-in-string": [ |
||||
"error" |
||||
] |
||||
} |
||||
} |
||||
|
@ -1,7 +1,7 @@ |
||||
export default function ErrorAlert({ error }) { |
||||
return error && ( |
||||
<div className="alert alert-danger" role="alert"> |
||||
<div className='alert alert-danger' role='alert'> |
||||
{error} |
||||
</div> |
||||
) |
||||
); |
||||
} |
||||
|
@ -1,19 +0,0 @@ |
||||
import React from "react" |
||||
import ContentLoader from "react-content-loader" |
||||
|
||||
const LoadingPlaceholder = (props) => ( |
||||
<ContentLoader
|
||||
speed={2} |
||||
width={"100%"} |
||||
height={30} |
||||
viewBox="0 0 100% 10" |
||||
backgroundColor="#2e2e2e" |
||||
foregroundColor="#5a5a5a" |
||||
{...props} |
||||
> |
||||
<rect x="0" y="10" rx="5" width="100%" height="10" /> |
||||
</ContentLoader> |
||||
); |
||||
|
||||
export default LoadingPlaceholder; |
||||
|
@ -1,202 +1,119 @@ |
||||
import Image from 'next/image'; |
||||
// TODO: Remove once https://github.com/vercel/next.js/issues/52216 is resolved.
|
||||
// next/image` seems to be affected by a default + named export bundling bug.
|
||||
let ResolvedImage = Image; |
||||
if ('default' in ResolvedImage) { |
||||
ResolvedImage = ResolvedImage.default; |
||||
} |
||||
import Link from 'next/link'; |
||||
import { withRouter } from 'next/router'; |
||||
|
||||
export default withRouter(function MenuLinks({ router }) { |
||||
return (<> |
||||
<Link href="/"> |
||||
<a className="d-flex align-items-center mb-3 mb-md-0 text-body text-decoration-none"> |
||||
<Image src="/favicon.ico" width="32" height="32" alt=" " /> |
||||
<span className="mx-2 fs-4 text-decoration-none">BasedFlare</span> |
||||
</a> |
||||
<Link href='/' className='d-flex align-items-center mb-3 mb-md-0 text-body text-decoration-none'> |
||||
<ResolvedImage src='/favicon.ico' width='32' height='32' alt=' ' /> |
||||
<span className='mx-2 fs-4 text-decoration-none'>BasedFlare</span> |
||||
</Link> |
||||
<hr /> |
||||
<ul className="nav nav-pills flex-column mb-auto"> |
||||
{/*<li className="nav-item"> |
||||
<Link href="/"> |
||||
<a className={router.pathname === "/" ? "nav-link active" : "nav-link"} aria-current="page"> |
||||
<i className="bi-house-door pe-none me-2" width="16" height="16" /> |
||||
Home |
||||
</a> |
||||
</Link> |
||||
</li>*/} |
||||
<li className="nav-item"> |
||||
<Link href="/account"> |
||||
<a className={router.pathname === "/account" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-person-square pe-none me-2" width="16" height="16" /> |
||||
<ul className='nav nav-pills flex-column mb-auto'> |
||||
<li className='nav-item'> |
||||
<Link href='/account' className={router.pathname === '/account' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-person-square pe-none me-2' width='16' height='16' /> |
||||
Account |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/domains"> |
||||
<a className={router.pathname === "/domains" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-layers pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/domains' className={router.pathname === '/domains' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-layers pe-none me-2' width='16' height='16' /> |
||||
Domains |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
{/*<li className="nav-item"> |
||||
<Link href="/dns"> |
||||
<a className={router.pathname === "/dns" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-card-list pe-none me-2" width="16" height="16" /> |
||||
DNS |
||||
</a> |
||||
</Link> |
||||
</li>*/} |
||||
{/*<li className="nav-item"> |
||||
<Link href="/clusters"> |
||||
<a className={router.pathname === "/clusters" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-clouds pe-none me-2" width="16" height="16" /> |
||||
Clusters |
||||
</a> |
||||
</Link> |
||||
</li>*/} |
||||
{/*process.env.NEXT_PUBLIC_CUSTOM_BACKENDS_ENABLED && <li className="nav-item"> |
||||
<Link href="/map/backends"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "backends" ? "nav-link active" : "nav-link"} aria-current="page"> |
||||
<i className="bi-hdd-network pe-none me-2" width="16" height="16" /> |
||||
Internal Backends |
||||
</a> |
||||
</Link> |
||||
</li>*/} |
||||
<li className="nav-item"> |
||||
<Link href="/map/hosts"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "hosts" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-hdd-network pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/hosts' className={router.pathname === '/map/[name]' && router.query.name === 'hosts' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-hdd-network pe-none me-2' width='16' height='16' /> |
||||
Backends |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/certs"> |
||||
<a className={router.pathname === "/certs" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-file-earmark-lock pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/certs' className={router.pathname === '/certs' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-file-earmark-lock pe-none me-2' width='16' height='16' /> |
||||
HTTPS Certificates |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/csr"> |
||||
<a className={router.pathname === "/csr" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-building-lock pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/csr' className={router.pathname === '/csr' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-building-lock pe-none me-2' width='16' height='16' /> |
||||
Origin CSR |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/ddos_config"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "ddos_config" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-sliders2 pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/ddos_config' className={router.pathname === '/map/[name]' && router.query.name === 'ddos_config' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-sliders2 pe-none me-2' width='16' height='16' /> |
||||
Protection Settings |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/ddos"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "ddos" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-shield-check pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/ddos' className={router.pathname === '/map/[name]' && router.query.name === 'ddos' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-shield-check pe-none me-2' width='16' height='16' /> |
||||
Protection Rules |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/rewrite"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "rewrite" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-pencil pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/rewrite' className={router.pathname === '/map/[name]' && router.query.name === 'rewrite' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-pencil pe-none me-2' width='16' height='16' /> |
||||
Rewrites |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/redirect"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "redirect" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-signpost pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/redirect' className={router.pathname === '/map/[name]' && router.query.name === 'redirect' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-signpost pe-none me-2' width='16' height='16' /> |
||||
Redirects |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/whitelist"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "whitelist" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-person-check pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/whitelist' className={router.pathname === '/map/[name]' && router.query.name === 'whitelist' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-person-check pe-none me-2' width='16' height='16' /> |
||||
IP Whitelist |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/blockedip"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "blockedip" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-person-slash pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/blockedip' className={router.pathname === '/map/[name]' && router.query.name === 'blockedip' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-person-slash pe-none me-2' width='16' height='16' /> |
||||
IP Blacklist |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/blockedasn"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "blockedasn" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-building-slash pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/blockedasn' className={router.pathname === '/map/[name]' && router.query.name === 'blockedasn' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-building-slash pe-none me-2' width='16' height='16' /> |
||||
ASN Blacklist |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/map/maintenance"> |
||||
<a className={router.pathname === "/map/[name]" && router.query.name === "maintenance" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-info-square pe-none me-2" width="16" height="16" /> |
||||
<li className='nav-item'> |
||||
<Link href='/map/maintenance' className={router.pathname === '/map/[name]' && router.query.name === 'maintenance' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-info-square pe-none me-2' width='16' height='16' /> |
||||
Maintenance Mode |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
{/*<li className="nav-item"> |
||||
<Link href="/stats"> |
||||
<a className={router.pathname === "/stats" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-table pe-none me-2" width="16" height="16" /> |
||||
Statistics |
||||
</a> |
||||
</Link> |
||||
</li>*/} |
||||
</ul> |
||||
<hr /> |
||||
<ul className="nav nav-pills flex-column"> |
||||
<li className="nav-item user-select-none"> |
||||
<Link href="/onboarding"> |
||||
<a className={router.pathname === "/onboarding" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-rocket-takeoff pe-none me-2" width="16" height="16" /> |
||||
<ul className='nav nav-pills flex-column'> |
||||
<li className='nav-item user-select-none'> |
||||
<Link href='/onboarding' className={router.pathname === '/onboarding' ? 'nav-link active' : 'nav-link text-body'} aria-current='page'> |
||||
<i className='bi-rocket-takeoff pe-none me-2' width='16' height='16' /> |
||||
Onboarding |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
</ul> |
||||
{/*<hr /> |
||||
<ul className="nav nav-pills flex-column"> |
||||
<li className="nav-item"> |
||||
<Link href="/login"> |
||||
<a className={router.pathname === "/login" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-door-closed pe-none me-2" width="16" height="16" /> |
||||
Login |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
<li className="nav-item"> |
||||
<Link href="/register"> |
||||
<a className={router.pathname === "/register" ? "nav-link active" : "nav-link text-body"} aria-current="page"> |
||||
<i className="bi-person-plus pe-none me-2" width="16" height="16" /> |
||||
Register |
||||
</a> |
||||
</Link> |
||||
</li> |
||||
</ul>*/} |
||||
<hr /> |
||||
<ul className="nav nav-pills flex-column"> |
||||
<li className="nav-item"> |
||||
<form action="/forms/logout" method="POST"> |
||||
<button className="nav-link text-body" type="submit"> |
||||
<i className="bi-door-open pe-none me-2" width="16" height="16" /> |
||||
<ul className='nav nav-pills flex-column'> |
||||
<li className='nav-item'> |
||||
<form action='/forms/logout' method='POST'> |
||||
<button className='nav-link text-body' type='submit'> |
||||
<i className='bi-door-open pe-none me-2' width='16' height='16' /> |
||||
Logout |
||||
</button> |
||||
</form> |
||||
</li> |
||||
</ul> |
||||
</>); |
||||
}) |
||||
}); |
||||
|
@ -1,12 +1,16 @@ |
||||
const { MongoClient } = require('mongodb'); |
||||
import { MongoClient } from 'mongodb'; |
||||
|
||||
module.exports = { |
||||
let _client; |
||||
|
||||
connect: async () => { |
||||
const client = new MongoClient(process.env.DB_URL); |
||||
await client.connect(); |
||||
module.exports.client = client; |
||||
module.exports.db = client.db(); |
||||
} |
||||
export async function connect() { |
||||
_client = new MongoClient(process.env.DB_URL); |
||||
await _client.connect(); |
||||
} |
||||
|
||||
export function client() { |
||||
return _client; |
||||
} |
||||
|
||||
export function db() { |
||||
return _client && _client.db(); |
||||
} |
||||
|
@ -1,3 +1,3 @@ |
||||
module.exports = { |
||||
export default { |
||||
/* config options here */ |
||||
} |
||||
}; |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,149 @@ |
||||
:root { font-family: 'Inter',arial,Helvetica,sans-serif; } |
||||
@supports (font-variation-settings: normal) { |
||||
:root { font-family: 'Inter var',arial,Helvetica,sans-serif; } |
||||
} |
||||
html, body { font-family: 'Inter',arial,Helvetica,sans-serif; height: 100%; overflow: hidden; background: #F4F5F7; } |
||||
.sidebar { background: var(--bs-body-bg); } |
||||
.corner-ribbon {z-index:9999; width: 180px;top: 8px;left: auto;text-align: center;line-height: 30px;letter-spacing: 1px;color: white;background: darkorange;box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);right: -70px;transform: rotate(45deg);-webkit-transform: rotate(46deg);position: fixed;overflow: hidden;} |
||||
.green { color: green; } |
||||
.red { color: red; } |
||||
footer { margin-top: auto; } |
||||
.btn { font-weight: bold; } |
||||
.nav-item:not(:first-child) { margin-top: 10px; } |
||||
.nav-link { color: white; } |
||||
.nav-link:hover { color: #6aa6fd; } |
||||
.mobile-menu { margin: 0 -16px; } |
||||
.fs-xs { font-size: small; } |
||||
.table, .list-group { box-shadow: 0 0px 3px rgba(0,0,0,.1); max-width: 100%; min-width: 600px; background-color: var(--bs-body-bg); } |
||||
.text-decoration-none { color: var(--bs-body-color); } |
||||
.sidebar { box-shadow: 0 0px 3px rgba(0,0,0,0.2); } |
||||
.card { background: var(--bs-body-bg) !important; color: var(--bs-body-color) !important; } |
||||
.table { margin-bottom: 0; } |
||||
.table-responsive { box-shadow: 0 0px 3px rgba(0,0,0,0.2); } |
||||
a.text-success:visited, a.text-success:hover { color: rgba(var(--bs-success-rgb),var(--bs-text-opacity)) !important } |
||||
.select__control { |
||||
transition: none; |
||||
} |
||||
.form-control::placeholder { |
||||
color: #6c757d!important; |
||||
} |
||||
input:autofill, input:-webkit-autofill { |
||||
background-color: initial!important; |
||||
background-image: initial!important; |
||||
color: var(--bs-body-color)!important; |
||||
} |
||||
.select__control:hover:not(.select__control--is-focused) { |
||||
border-color: #ced4da; |
||||
} |
||||
.select__control--menu-is-open, .select__control--is-focused { |
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); |
||||
border-color: #86b7fe!important; |
||||
} |
||||
tr:target, tr:target textarea { |
||||
background: var(--bs-highlight-bg); |
||||
} |
||||
table.notaborder textarea { |
||||
border: none!important; |
||||
} |
||||
@media (max-width: 650px) { |
||||
.table, .list-group { min-width: unset; } |
||||
} |
||||
@media (min-width: 800px) { |
||||
.mobile-btn { display: none!important; } |
||||
} |
||||
@media (max-width: 800px) { |
||||
.sidebar { display: none; } |
||||
} |
||||
@media (prefers-color-scheme: dark) { |
||||
:root { |
||||
--bs-body-color: #fff; |
||||
--bs-body-bg: #23272a; |
||||
--bs-secondary-color: #fff; |
||||
} |
||||
table * { |
||||
--bs-table-accent-bg: #2c2f33; |
||||
--bs-table-color-state: #fff; |
||||
} |
||||
html, body { background: var(--bs-body-bg); } |
||||
.nav-pills { |
||||
--bs-nav-pills-link-active-bg: #7289da; |
||||
--bs-btn-hover-bg: #7289da; |
||||
--bs-btn-hover-border-color: #7289da; |
||||
} |
||||
.btn-primary { |
||||
--bs-btn-bg: #7289da; |
||||
--bs-btn-border-color: #7289da; |
||||
--bs-btn-hover-bg: #6481e7; |
||||
--bs-btn-hover-border-color: #6481e7; |
||||
--bs-btn-active-bg: #6481e7; |
||||
--bs-btn-active-border-color: #6481e7; |
||||
--bs-btn-disabled-bg: #7289da; |
||||
--bs-btn-disabled-border-color: #7289da; |
||||
} |
||||
.badge.bg-primary { |
||||
background-color: #7289da; |
||||
} |
||||
.text-muted, a, a:visited, a:hover, .nav-link, .nav-link:hover { color:#fff!important; } |
||||
.list-group-item { color: #fff; background-color: #2c2f33!important; border-color: var(--bs-body-bg) } |
||||
input:not(.btn):not(.select__input), option, select.form-select, textarea, .input-group-text { color: #fff!important; background-color: #2c2f33!important; border: 1px solid black!important; } |
||||
.list-group-item-action:focus, .list-group-item-action:hover { color: #fff; background-color: #1F1F1F; } |
||||
.sidebar, .table { background-color: #2c2f33!important; } |
||||
.table { border-color: var(--bs-gray-900)!important; } |
||||
tr:target { |
||||
background: #ffc10720!important; |
||||
} |
||||
tr:target textarea { |
||||
background: transparent!important; |
||||
} |
||||
.select__control { |
||||
background-color: #393939; |
||||
border-color: var(--bs-gray-900); |
||||
transition: none; |
||||
} |
||||
.select__input { |
||||
color: white!important; |
||||
} |
||||
.select__control:hover:not(.select__control--is-focused) { |
||||
border-color: black; |
||||
} |
||||
.select__control--is-focused, |
||||
.select__control--menu-is-open { |
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); |
||||
border-color: black!important; |
||||
} |
||||
.select__multi-value { |
||||
background-color: var(--bs-dark); |
||||
border-radius: 5px; |
||||
} |
||||
.select__multi-value__label { |
||||
padding: 2px 10px; |
||||
color: #fff; |
||||
} |
||||
.select__menu { |
||||
background-color: #393939; |
||||
border: 1px solid var(--bs-gray-900); |
||||
} |
||||
.select__option { |
||||
background-color: #393939; |
||||
|
||||
} |
||||
.select__option:hover { |
||||
background-color: var(--bs-dark); |
||||
} |
||||
.select__indicator-separator { |
||||
background-color: #393939; |
||||
} |
||||
.select__clear-indicator:hover { |
||||
color: #fff; |
||||
} |
||||
.select__multi-value__remove:hover { |
||||
background-color: #5D2F24; |
||||
color: #DE350B; |
||||
border-radius: 0 5px 5px 0; |
||||
} |
||||
.select__placeholder, |
||||
.select__single-value { |
||||
color: #fff; |
||||
} |
||||
} |
||||
|
@ -1,66 +1,61 @@ |
||||
'use strict'; |
||||
|
||||
const Redis = require('ioredis') |
||||
, client = new Redis({ |
||||
host: process.env.REDIS_HOST || '127.0.0.1', |
||||
port: process.env.REDIS_PORT || 6379, |
||||
password: process.env.REDIS_PASS || '', |
||||
db: 0, |
||||
}) |
||||
, lockClient = new Redis({ |
||||
host: process.env.REDIS_HOST || '127.0.0.1', |
||||
port: process.env.REDIS_PORT || 6379, |
||||
password: process.env.REDIS_PASS || '', |
||||
db: 1, |
||||
}); |
||||
|
||||
module.exports = { |
||||
|
||||
client, |
||||
lockClient, |
||||
|
||||
close: () => { |
||||
client.quit(); |
||||
lockClient.quit(); |
||||
}, |
||||
|
||||
//get a value with key
|
||||
get: (key) => { |
||||
return client.get(key).then(res => { return JSON.parse(res); }); |
||||
}, |
||||
|
||||
//get a hash value
|
||||
hgetall: (key) => { |
||||
return client.hgetall(key).then(res => { return res }); |
||||
}, |
||||
|
||||
//get a hash value
|
||||
hget: (key, hash) => { |
||||
return client.hget(key, hash).then(res => { return JSON.parse(res); }); |
||||
}, |
||||
|
||||
//set a hash value
|
||||
hset: (key, hash, value) => { |
||||
return client.hset(key, hash, JSON.stringify(value)); |
||||
}, |
||||
|
||||
//delete a hash
|
||||
hdel: (key, hash) => { |
||||
return client.hdel(key, hash); |
||||
}, |
||||
|
||||
//set a value on key
|
||||
set: (key, value) => { |
||||
return client.set(key, JSON.stringify(value)); |
||||
}, |
||||
|
||||
//delete value with key
|
||||
del: (keyOrKeys) => { |
||||
if (Array.isArray(keyOrKeys)) { |
||||
return client.del(...keyOrKeys); |
||||
} else { |
||||
return client.del(keyOrKeys); |
||||
} |
||||
}, |
||||
|
||||
}; |
||||
import Redis from 'ioredis';; |
||||
|
||||
export const client = new Redis({ |
||||
host: process.env.REDIS_HOST || '127.0.0.1', |
||||
port: process.env.REDIS_PORT || 6379, |
||||
password: process.env.REDIS_PASS || '', |
||||
db: 0, |
||||
}); |
||||
|
||||
export const lockClient = new Redis({ |
||||
host: process.env.REDIS_HOST || '127.0.0.1', |
||||
port: process.env.REDIS_PORT || 6379, |
||||
password: process.env.REDIS_PASS || '', |
||||
db: 1, |
||||
}); |
||||
|
||||
export function close() { |
||||
client.quit(); |
||||
lockClient.quit(); |
||||
} |
||||
|
||||
//get a value with key
|
||||
export function get(key) { |
||||
return client.get(key).then(res => { return JSON.parse(res); }); |
||||
} |
||||
|
||||
//get a hash value
|
||||
export function hgetall(key) { |
||||
return client.hgetall(key).then(res => { return res; }); |
||||
} |
||||
|
||||
//get a hash value
|
||||
export function hget(key, hash) { |
||||
return client.hget(key, hash).then(res => { return JSON.parse(res); }); |
||||
} |
||||
|
||||
//set a hash value
|
||||
export function hset(key, hash, value) { |
||||
return client.hset(key, hash, JSON.stringify(value)); |
||||
} |
||||
|
||||
//delete a hash
|
||||
export function hdel(key, hash) { |
||||
return client.hdel(key, hash); |
||||
} |
||||
|
||||
//set a value on key
|
||||
export function set(key, value) { |
||||
return client.set(key, JSON.stringify(value)); |
||||
} |
||||
|
||||
//delete value with key
|
||||
export function del(keyOrKeys) { |
||||
if (Array.isArray(keyOrKeys)) { |
||||
return client.del(...keyOrKeys); |
||||
} else { |
||||
return client.del(keyOrKeys); |
||||
} |
||||
} |
||||
|
@ -1,262 +1,604 @@ |
||||
const express = require('express') |
||||
, dev = process.env.NODE_ENV !== 'production' |
||||
, session = require('express-session') |
||||
, MongoStore = require('connect-mongo') |
||||
, db = require('./db.js') |
||||
, csrf = require('csurf') |
||||
, OpenAPIClientAxios = require('openapi-client-axios').default |
||||
, { dynamicResponse } = require('./util.js') |
||||
, definition = require('./specification_openapiv3.js') |
||||
, fetch = require('node-fetch') |
||||
, FormData = require('form-data') |
||||
, update = require('./update.js') |
||||
, agent = require('./agent.js'); |
||||
import express from 'express'; |
||||
import session from 'express-session'; |
||||
import MongoStore from 'connect-mongo'; |
||||
import csrf from 'csurf'; |
||||
import OpenAPIClientAxios from 'openapi-client-axios'; |
||||
import fetch from 'node-fetch'; |
||||
import FormData from 'form-data'; |
||||
|
||||
const testRouter = (server, app) => { |
||||
import * as db from './db.js'; |
||||
import { dynamicResponse } from './util.js'; |
||||
import definition from './specification_openapiv3.js'; |
||||
import * as update from './update.js'; |
||||
import * as agent from './agent.js'; |
||||
|
||||
const sessionStore = session({ |
||||
secret: process.env.COOKIE_SECRET, |
||||
store: MongoStore.create({ mongoUrl: process.env.DB_URL }), |
||||
resave: false, |
||||
saveUninitialized: false, |
||||
rolling: true, |
||||
cookie: { |
||||
httpOnly: true, |
||||
secure: !dev, //TODO: check https
|
||||
sameSite: 'strict', |
||||
maxAge: 1000 * 60 * 60 * 24 * 30, //month
|
||||
} |
||||
}); |
||||
import * as accountController from './controllers/account.js'; |
||||
import * as mapsController from './controllers/maps.js'; |
||||
import * as clustersController from './controllers/clusters.js'; |
||||
import * as certsController from './controllers/certs.js'; |
||||
import * as dnsController from './controllers/dns.js'; |
||||
import * as domainsController from './controllers/domains.js'; |
||||
|
||||
const useSession = (req, res, next) => { |
||||
sessionStore(req, res, next); |
||||
}; |
||||
const dev = process.env.NODE_ENV !== 'production'; |
||||
|
||||
const fetchSession = async (req, res, next) => { |
||||
if (req.session.user) { |
||||
const account = await db.db.collection('accounts') |
||||
.findOne({ _id: req.session.user }); |
||||
if (account) { |
||||
const numCerts = await db.db.collection('certs') |
||||
.countDocuments({ username: account._id }); |
||||
const strippedClusters = account.clusters |
||||
.map(c => { |
||||
return c.split(',') |
||||
.map(clusterString => { |
||||
const clusterUrl = new URL(clusterString); |
||||
clusterUrl.username = ''; |
||||
clusterUrl.password = ''; |
||||
return clusterUrl.toString(); |
||||
}) |
||||
.join(','); |
||||
}); |
||||
res.locals.clusters = account.clusters; |
||||
res.locals.user = { |
||||
username: account._id, |
||||
domains: account.domains, |
||||
clusters: strippedClusters, |
||||
activeCluster: account.activeCluster, |
||||
onboarding: account.onboarding, |
||||
numCerts, |
||||
}; |
||||
return next(); |
||||
} |
||||
req.session.destroy(); |
||||
} |
||||
next(); |
||||
}; |
||||
export default function router(server, app) { |
||||
const sessionStore = session({ |
||||
secret: process.env.COOKIE_SECRET, |
||||
store: MongoStore.create({ mongoUrl: process.env.DB_URL }), |
||||
resave: false, |
||||
saveUninitialized: false, |
||||
rolling: true, |
||||
cookie: { |
||||
httpOnly: true, |
||||
secure: !dev, //TODO: check https
|
||||
sameSite: 'strict', |
||||
maxAge: 1000 * 60 * 60 * 24 * 30, //month
|
||||
}, |
||||
}); |
||||
|
||||
const checkSession = (req, res, next) => { |
||||
if (!res.locals.user) { |
||||
return dynamicResponse(req, res, 302, { redirect: '/login' }); |
||||
} |
||||
next(); |
||||
}; |
||||
const useSession = (req, res, next) => { |
||||
sessionStore(req, res, next); |
||||
}; |
||||
|
||||
const checkOnboarding = (req, res, next) => { |
||||
if (!res.locals.user || res.locals.user.onboarding === false) { |
||||
return dynamicResponse(req, res, 302, { redirect: '/onboarding' }); |
||||
const fetchSession = async (req, res, next) => { |
||||
if (req.session.user) { |
||||
const account = await db.db().collection('accounts') |
||||
.findOne({ _id: req.session.user }); |
||||
if (account) { |
||||
const numCerts = await db.db().collection('certs') |
||||
.countDocuments({ username: account._id }); |
||||
const strippedClusters = account.clusters |
||||
.map((c) => { |
||||
return c.split(',') |
||||
.map((clusterString) => { |
||||
const clusterUrl = new URL(clusterString); |
||||
clusterUrl.username = ''; |
||||
clusterUrl.password = ''; |
||||
return clusterUrl.toString(); |
||||
}) |
||||
.join(','); |
||||
}); |
||||
res.locals.clusters = account.clusters; |
||||
res.locals.user = { |
||||
username: account._id, |
||||
domains: account.domains, |
||||
clusters: strippedClusters, |
||||
activeCluster: account.activeCluster, |
||||
onboarding: account.onboarding, |
||||
numCerts, |
||||
}; |
||||
return next(); |
||||
} |
||||
next(); |
||||
}; |
||||
req.session.destroy(); |
||||
} |
||||
next(); |
||||
}; |
||||
|
||||
const csrfMiddleware = csrf(); |
||||
const checkSession = (req, res, next) => { |
||||
if (!res.locals.user) { |
||||
return dynamicResponse(req, res, 302, { redirect: '/login' }); |
||||
} |
||||
next(); |
||||
}; |
||||
|
||||
//dataplaneapi middleware
|
||||
const useHaproxy = (req, res, next) => { |
||||
if (res.locals.clusters.length === 0) { |
||||
return next(); |
||||
} |
||||
try { |
||||
res.locals.fMap = server.locals.fMap; |
||||
res.locals.mapValueNames = server.locals.mapValueNames; |
||||
const clusterUrls = res.locals.clusters[res.locals.user.activeCluster] |
||||
.split(',') |
||||
.map(u => new URL(u)); |
||||
const firstClusterURL = clusterUrls[0]; |
||||
const checkOnboarding = (req, res, next) => { |
||||
if (!res.locals.user || res.locals.user.onboarding === false) { |
||||
return dynamicResponse(req, res, 302, { redirect: '/onboarding' }); |
||||
} |
||||
next(); |
||||
}; |
||||
|
||||
const csrfMiddleware = csrf(); |
||||
|
||||
//dataplaneapi middleware
|
||||
const useHaproxy = (req, res, next) => { |
||||
if (res.locals.clusters.length === 0) { |
||||
return next(); |
||||
} |
||||
try { |
||||
res.locals.fMap = server.locals.fMap; |
||||
res.locals.mapValueNames = server.locals.mapValueNames; |
||||
const clusterUrls = res.locals.clusters[res.locals.user.activeCluster] |
||||
.split(',') |
||||
.map((u) => new URL(u)); |
||||
const firstClusterURL = clusterUrls[0]; |
||||
|
||||
//NOTE: all servers in cluster must have same credentials for now
|
||||
const base64Auth = Buffer.from(`${firstClusterURL.username}:${firstClusterURL.password}`).toString("base64"); |
||||
const api = new OpenAPIClientAxios({ |
||||
//definition: `${firstClusterURL.origin}/v2/specification_openapiv3`,
|
||||
definition, |
||||
axiosConfigDefaults: { |
||||
httpsAgent: agent, |
||||
headers: { |
||||
'authorization': `Basic ${base64Auth}`, |
||||
//NOTE: all servers in cluster must have same credentials for now
|
||||
const base64Auth = Buffer.from( |
||||
`${firstClusterURL.username}:${firstClusterURL.password}`, |
||||
).toString('base64'); |
||||
const api = new OpenAPIClientAxios.default({ |
||||
//definition: `${firstClusterURL.origin}/v2/specification_openapiv3`,
|
||||
definition, |
||||
axiosConfigDefaults: { |
||||
httpsAgent: agent, |
||||
headers: { |
||||
'authorization': `Basic ${base64Auth}`, |
||||
}, |
||||
}, |
||||
}); |
||||
const apiInstance = api.initSync(); |
||||
apiInstance.defaults.baseURL = `${firstClusterURL.origin}/v2`; |
||||
res.locals.dataPlane = apiInstance; |
||||
async function dataPlaneRetry(operationId, ...args) { |
||||
let retryCnt = 0; |
||||
function run() { |
||||
return apiInstance[operationId](...args).catch(function (err) { |
||||
if ( |
||||
operationId === 'getRuntimeMapEntry' && err && err.response && |
||||
err.response.data && err.response.data.code === 404 |
||||
) { |
||||
return null; |
||||
} |
||||
} |
||||
}); |
||||
const apiInstance = api.initSync(); |
||||
apiInstance.defaults.baseURL = `${firstClusterURL.origin}/v2`; |
||||
res.locals.dataPlane = apiInstance; |
||||
async function dataPlaneRetry(operationId, ...args) { |
||||
let retryCnt = 0; |
||||
function run() { |
||||
return apiInstance[operationId](...args).catch(function (err) { |
||||
if (operationId === 'getRuntimeMapEntry' && err && err.response |
||||
&& err.response.data && err.response.data.code === 404) { |
||||
return null; |
||||
} |
||||
++retryCnt; |
||||
console.error('dataPlaneRetry retry', retryCnt, ' after error', err); |
||||
console.trace(); |
||||
apiInstance.defaults.baseURL = `${clusterUrls[retryCnt].origin}/v2`; |
||||
if (retryCnt > clusterUrls.length-1) { |
||||
console.error('Max retries exceeded in dataPlaneRetry', err.message); |
||||
throw err; |
||||
} |
||||
return run(); |
||||
}); |
||||
} |
||||
return run(); |
||||
++retryCnt; |
||||
console.error( |
||||
'dataPlaneRetry retry', |
||||
retryCnt, |
||||
' after error', |
||||
err, |
||||
); |
||||
console.trace(); |
||||
apiInstance.defaults.baseURL = `${clusterUrls[retryCnt].origin}/v2`; |
||||
if (retryCnt > clusterUrls.length - 1) { |
||||
console.error( |
||||
'Max retries exceeded in dataPlaneRetry', |
||||
err.message, |
||||
); |
||||
throw err; |
||||
} |
||||
return run(); |
||||
}); |
||||
} |
||||
res.locals.dataPlaneRetry = dataPlaneRetry; |
||||
return run(); |
||||
} |
||||
res.locals.dataPlaneRetry = dataPlaneRetry; |
||||
|
||||
res.locals.dataPlaneAll = async (operationId, parameters, data, config, all=false) => { |
||||
const promiseResults = await Promise.all(clusterUrls.map(clusterUrl => { |
||||
const singleApi = new OpenAPIClientAxios({ definition, axiosConfigDefaults: { httpsAgent: agent, headers: { 'authorization': `Basic ${base64Auth}` } } }); |
||||
res.locals.dataPlaneAll = async ( |
||||
operationId, |
||||
parameters, |
||||
data, |
||||
config, |
||||
all = false, |
||||
) => { |
||||
const promiseResults = await Promise.all( |
||||
clusterUrls.map((clusterUrl) => { |
||||
const singleApi = new OpenAPIClientAxios.default({ |
||||
definition, |
||||
axiosConfigDefaults: { |
||||
httpsAgent: agent, |
||||
headers: { 'authorization': `Basic ${base64Auth}` }, |
||||
}, |
||||
}); |
||||
const singleApiInstance = singleApi.initSync(); |
||||
singleApiInstance.defaults.baseURL = `${clusterUrl.origin}/v2`; |
||||
return singleApiInstance[operationId](parameters, data, { ...config, baseUrl: `${clusterUrl.origin}/v2` }); |
||||
})); |
||||
return all ? promiseResults.map(p => p.data) : promiseResults[0]; //TODO: better desync handling
|
||||
} |
||||
res.locals.postFileAll = async (path, options, file, fdOptions) => { |
||||
//used for stuff that dataplaneapi with axios seems to struggle with e.g. multipart body
|
||||
const promiseResults = await Promise.all(clusterUrls.map(clusterUrl => { |
||||
return singleApiInstance[operationId](parameters, data, { |
||||
...config, |
||||
baseUrl: `${clusterUrl.origin}/v2`, |
||||
}); |
||||
}), |
||||
); |
||||
return all ? promiseResults.map((p) => p.data) : promiseResults[0]; //TODO: better desync handling
|
||||
}; |
||||
res.locals.postFileAll = async (path, options, file, fdOptions) => { |
||||
//used for stuff that dataplaneapi with axios seems to struggle with e.g. multipart body
|
||||
const promiseResults = await Promise.all( |
||||
clusterUrls.map((clusterUrl) => { |
||||
const fd = new FormData(); //must resonctruct each time, or get a socket hang up
|
||||
fd.append('file_upload', file, fdOptions); |
||||
return fetch(`${clusterUrl.origin}${path}`, { ...options, body: fd, agent }).then(resp => resp.json()); |
||||
})); |
||||
return promiseResults[0]; //TODO: better desync handling
|
||||
} |
||||
next(); |
||||
} catch (e) { |
||||
console.error(e) |
||||
return dynamicResponse(req, res, 500, { error: e }); |
||||
} |
||||
}; |
||||
return fetch(`${clusterUrl.origin}${path}`, { |
||||
...options, |
||||
body: fd, |
||||
agent, |
||||
}).then((resp) => resp.json()); |
||||
}), |
||||
); |
||||
return promiseResults[0]; //TODO: better desync handling
|
||||
}; |
||||
next(); |
||||
} catch (e) { |
||||
console.error(e); |
||||
return dynamicResponse(req, res, 500, { error: e }); |
||||
} |
||||
}; |
||||
|
||||
const hasCluster = (req, res, next) => { |
||||
if (res.locals.user.clusters.length > 0 || (req.baseUrl+req.path) === '/forms/cluster/add') { |
||||
return next(); |
||||
} |
||||
return dynamicResponse(req, res, 302, { redirect: '/clusters' }); |
||||
}; |
||||
const hasCluster = (req, res, next) => { |
||||
if ( |
||||
res.locals.user.clusters.length > 0 || |
||||
(req.baseUrl + req.path) === '/forms/cluster/add' |
||||
) { |
||||
return next(); |
||||
} |
||||
return dynamicResponse(req, res, 302, { redirect: '/clusters' }); |
||||
}; |
||||
|
||||
//Controllers
|
||||
const accountController = require('./controllers/account') |
||||
, mapsController = require('./controllers/maps') |
||||
, clustersController = require('./controllers/clusters') |
||||
, certsController = require('./controllers/certs') |
||||
, dnsController = require('./controllers/dns') |
||||
, domainsController = require('./controllers/domains'); |
||||
//unauthed pages
|
||||
server.get('/', useSession, fetchSession, (req, res, next) => { |
||||
return app.render(req, res, '/index'); |
||||
}); |
||||
server.get('/login', useSession, fetchSession, (req, res, next) => { |
||||
return app.render(req, res, '/login'); |
||||
}); |
||||
server.get('/register', useSession, fetchSession, (req, res, next) => { |
||||
return app.render(req, res, '/register'); |
||||
}); |
||||
|
||||
//unauthed pages
|
||||
server.get('/', useSession, fetchSession, (req, res, next) => { return app.render(req, res, '/index') }); |
||||
server.get('/login', useSession, fetchSession, (req, res, next) => { return app.render(req, res, '/login') }); |
||||
server.get('/register', useSession, fetchSession, (req, res, next) => { return app.render(req, res, '/register') }); |
||||
//register/login/logout/onboarding forms
|
||||
server.post('/forms/login', useSession, accountController.login); |
||||
server.post( |
||||
'/forms/onboarding', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
accountController.finishOnboarding, |
||||
); |
||||
server.post('/forms/logout', useSession, accountController.logout); |
||||
server.post( |
||||
'/forms/register', |
||||
useSession, |
||||
fetchSession, |
||||
accountController.register, |
||||
); |
||||
|
||||
//register/login/logout/onboarding forms
|
||||
server.post('/forms/login', useSession, accountController.login); |
||||
server.post('/forms/onboarding', useSession, fetchSession, checkSession, accountController.finishOnboarding); |
||||
server.post('/forms/logout', useSession, accountController.logout); |
||||
server.post('/forms/register', useSession, fetchSession, accountController.register); |
||||
const mapNames = [ |
||||
process.env.BLOCKED_IP_MAP_NAME, |
||||
process.env.BLOCKED_ASN_MAP_NAME, |
||||
process.env.MAINTENANCE_MAP_NAME, |
||||
process.env.WHITELIST_MAP_NAME, |
||||
process.env.REDIRECT_MAP_NAME, |
||||
process.env.BACKENDS_MAP_NAME, |
||||
process.env.DDOS_MAP_NAME, |
||||
process.env.DDOS_CONFIG_MAP_NAME, |
||||
process.env.HOSTS_MAP_NAME, |
||||
process.env.REWRITE_MAP_NAME, |
||||
], |
||||
mapNamesOrString = mapNames.join('|'); |
||||
|
||||
const mapNames = [process.env.BLOCKED_IP_MAP_NAME, process.env.BLOCKED_ASN_MAP_NAME, process.env.MAINTENANCE_MAP_NAME, process.env.WHITELIST_MAP_NAME, process.env.REDIRECT_MAP_NAME, |
||||
process.env.BACKENDS_MAP_NAME, process.env.DDOS_MAP_NAME, process.env.DDOS_CONFIG_MAP_NAME, process.env.HOSTS_MAP_NAME, process.env.REWRITE_MAP_NAME] |
||||
, mapNamesOrString = mapNames.join('|'); |
||||
//authed pages
|
||||
server.get( |
||||
'/account', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
checkOnboarding, |
||||
useHaproxy, |
||||
csrfMiddleware, |
||||
accountController.accountPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/onboarding', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
csrfMiddleware, |
||||
accountController.onboardingPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/account.json', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
csrfMiddleware, |
||||
accountController.accountJson, |
||||
); |
||||
server.get( |
||||
`/map/:name(${mapNamesOrString})`, |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
checkOnboarding, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
mapsController.mapPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
`/map/:name(${mapNamesOrString}).json`, |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
mapsController.mapJson, |
||||
); |
||||
server.get( |
||||
'/clusters', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
checkOnboarding, |
||||
csrfMiddleware, |
||||
clustersController.clustersPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/clusters.json', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
clustersController.clustersJson, |
||||
); |
||||
server.get( |
||||
'/domains', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
domainsController.domainsPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/domains.json', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
domainsController.domainsJson, |
||||
); |
||||
server.get( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+)/new', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsRecordPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+).json', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsDomainJson, |
||||
); |
||||
server.get( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+)', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsDomainPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z]+).json', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsRecordJson, |
||||
); |
||||
server.get( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z]+)', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsRecordPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/certs', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
checkOnboarding, |
||||
useHaproxy, |
||||
csrfMiddleware, |
||||
certsController.certsPage.bind(null, app), |
||||
); |
||||
server.get( |
||||
'/certs.json', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
csrfMiddleware, |
||||
certsController.certsJson, |
||||
); |
||||
|
||||
//authed pages
|
||||
server.get('/account', useSession, fetchSession, checkSession, checkOnboarding, useHaproxy, csrfMiddleware, accountController.accountPage.bind(null, app)); |
||||
server.get('/onboarding', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, accountController.onboardingPage.bind(null, app)); |
||||
server.get('/account.json', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, accountController.accountJson); |
||||
server.get(`/map/:name(${mapNamesOrString})`, useSession, fetchSession, checkSession, checkOnboarding, useHaproxy, hasCluster, csrfMiddleware, mapsController.mapPage.bind(null, app)); |
||||
server.get(`/map/:name(${mapNamesOrString}).json`, useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, mapsController.mapJson); |
||||
server.get('/clusters', useSession, fetchSession, checkSession, checkOnboarding, csrfMiddleware, clustersController.clustersPage.bind(null, app)); |
||||
server.get('/clusters.json', useSession, fetchSession, checkSession, csrfMiddleware, clustersController.clustersJson); |
||||
server.get('/domains', useSession, fetchSession, checkSession, csrfMiddleware, domainsController.domainsPage.bind(null, app)); |
||||
server.get('/domains.json', useSession, fetchSession, checkSession, csrfMiddleware, domainsController.domainsJson); |
||||
server.get('/dns/:domain([a-zA-Z0-9-\.]+)/new', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsRecordPage.bind(null, app)); |
||||
server.get('/dns/:domain([a-zA-Z0-9-\.]+).json', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsDomainJson); |
||||
server.get('/dns/:domain([a-zA-Z0-9-\.]+)', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsDomainPage.bind(null, app)); |
||||
server.get('/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z]+).json', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsRecordJson); |
||||
server.get('/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z]+)', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsRecordPage.bind(null, app)); |
||||
server.get('/certs', useSession, fetchSession, checkSession, checkOnboarding, useHaproxy, csrfMiddleware, certsController.certsPage.bind(null, app)); |
||||
server.get('/certs.json', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, certsController.certsJson); |
||||
// server.get('/stats', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, accountController.statsPage.bind(null, app));
|
||||
// server.get('/stats.json', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, accountController.statsJson);
|
||||
const clusterRouter = express.Router({ caseSensitive: true }); |
||||
clusterRouter.post('/global/toggle', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, accountController.globalToggle); |
||||
clusterRouter.post(`/map/:name(${mapNamesOrString})/add`, useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, mapsController.patchMapForm); |
||||
clusterRouter.post(`/map/:name(${mapNamesOrString})/delete`, useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, mapsController.deleteMapForm); |
||||
clusterRouter.post('/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z_]+)/delete', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsRecordDelete); |
||||
clusterRouter.post('/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z_]+)', useSession, fetchSession, checkSession, csrfMiddleware, dnsController.dnsRecordUpdate); |
||||
clusterRouter.post('/cluster', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, clustersController.setCluster); |
||||
clusterRouter.post('/cluster/add', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, clustersController.addCluster); |
||||
clusterRouter.post('/cluster/delete', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, clustersController.deleteClusters); |
||||
clusterRouter.post('/domain/add', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, domainsController.addDomain); |
||||
clusterRouter.post('/domain/delete', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, domainsController.deleteDomain); |
||||
clusterRouter.post('/cert/add', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, certsController.addCert); |
||||
clusterRouter.post('/cert/upload', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, certsController.uploadCert); |
||||
clusterRouter.post('/cert/delete', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, certsController.deleteCert); |
||||
clusterRouter.post('/csr/verify', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, certsController.verifyUserCSR); |
||||
clusterRouter.post('/template', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, async (req, res, next) => { |
||||
if (res.locals.user.username !== "admin") { |
||||
const clusterRouter = express.Router({ caseSensitive: true }); |
||||
clusterRouter.post( |
||||
'/global/toggle', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
accountController.globalToggle, |
||||
); |
||||
clusterRouter.post( |
||||
`/map/:name(${mapNamesOrString})/add`, |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
mapsController.patchMapForm, |
||||
); |
||||
clusterRouter.post( |
||||
`/map/:name(${mapNamesOrString})/delete`, |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
mapsController.deleteMapForm, |
||||
); |
||||
clusterRouter.post( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z_]+)/delete', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsRecordDelete, |
||||
); |
||||
clusterRouter.post( |
||||
'/dns/:domain([a-zA-Z0-9-\.]+)/:zone([a-zA-Z0-9-\.@_]+)/:type([a-z_]+)', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
csrfMiddleware, |
||||
dnsController.dnsRecordUpdate, |
||||
); |
||||
clusterRouter.post( |
||||
'/cluster', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
clustersController.setCluster, |
||||
); |
||||
clusterRouter.post( |
||||
'/cluster/add', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
clustersController.addCluster, |
||||
); |
||||
clusterRouter.post( |
||||
'/cluster/delete', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
clustersController.deleteClusters, |
||||
); |
||||
clusterRouter.post( |
||||
'/domain/add', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
domainsController.addDomain, |
||||
); |
||||
clusterRouter.post( |
||||
'/domain/delete', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
domainsController.deleteDomain, |
||||
); |
||||
clusterRouter.post( |
||||
'/cert/add', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
certsController.addCert, |
||||
); |
||||
clusterRouter.post( |
||||
'/cert/upload', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
certsController.uploadCert, |
||||
); |
||||
clusterRouter.post( |
||||
'/cert/delete', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
useHaproxy, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
certsController.deleteCert, |
||||
); |
||||
clusterRouter.post( |
||||
'/csr/verify', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
certsController.verifyUserCSR, |
||||
); |
||||
clusterRouter.post( |
||||
'/template', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
async (req, res, next) => { |
||||
if (res.locals.user.username !== 'admin') { |
||||
return dynamicResponse(req, res, 403, { error: 'No permission' }); |
||||
} |
||||
const { id, data } = req.body |
||||
const { id, data } = req.body; |
||||
if (!id || !data) { |
||||
return dynamicResponse(req, res, 403, { error: 'Invalid input' }); |
||||
} |
||||
await db.db.collection('templates').updateOne({ _id: id }, { $set: { data } }, { upsert: true }); |
||||
await db.db().collection('templates').updateOne({ _id: id }, { |
||||
$set: { data }, |
||||
}, { upsert: true }); |
||||
return res.json({ ok: true }); |
||||
}); |
||||
clusterRouter.post('/update', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, async (req, res, next) => { |
||||
if (res.locals.user.username !== "admin") { |
||||
}, |
||||
); |
||||
clusterRouter.post( |
||||
'/update', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
async (req, res, next) => { |
||||
if (res.locals.user.username !== 'admin') { |
||||
return dynamicResponse(req, res, 403, { error: 'No permission' }); |
||||
} |
||||
await update(); |
||||
return res.json({ ok: true }); |
||||
}); |
||||
clusterRouter.post('/down', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, async (req, res, next) => { |
||||
if (res.locals.user.username !== "admin") { |
||||
}, |
||||
); |
||||
clusterRouter.post( |
||||
'/down', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
async (req, res, next) => { |
||||
if (res.locals.user.username !== 'admin') { |
||||
return dynamicResponse(req, res, 403, { error: 'No permission' }); |
||||
} |
||||
const ips = req.body.ips.filter(x => x && x.length > 0); |
||||
const ips = req.body.ips.filter((x) => x && x.length > 0); |
||||
if (ips.length === 0) { |
||||
await db.db.collection('down').updateOne({ _id: 'down' }, { $set: { ips: [] } }, { upsert: true }); |
||||
await db.db().collection('down').updateOne({ _id: 'down' }, { |
||||
$set: { ips: [] }, |
||||
}, { upsert: true }); |
||||
} else { |
||||
await db.db.collection('down').updateOne({ _id: 'down' }, { $addToSet: { ips: { '$each': ips } } }, { upsert: true }); |
||||
await db.db().collection('down').updateOne({ _id: 'down' }, { |
||||
$addToSet: { ips: { '$each': ips } }, |
||||
}, { upsert: true }); |
||||
} |
||||
return res.json({ ok: true }); |
||||
}); |
||||
clusterRouter.get('/csrf', useSession, fetchSession, checkSession, hasCluster, csrfMiddleware, (req, res, next) => { |
||||
}, |
||||
); |
||||
clusterRouter.get( |
||||
'/csrf', |
||||
useSession, |
||||
fetchSession, |
||||
checkSession, |
||||
hasCluster, |
||||
csrfMiddleware, |
||||
(req, res, next) => { |
||||
return res.send(req.csrfToken()); |
||||
}); |
||||
server.use('/forms', clusterRouter); |
||||
|
||||
}; |
||||
|
||||
module.exports = testRouter; |
||||
}, |
||||
); |
||||
server.use('/forms', clusterRouter); |
||||
} |
||||
|
File diff suppressed because one or more lines are too long
@ -1,40 +1,40 @@ |
||||
const db = require('./db.js'); |
||||
import * as db from './db.js'; |
||||
|
||||
exports.aTemplate = () => { |
||||
return db.db.collection('templates').findOne({ _id: 'a' }).then(res => res.data); |
||||
export function aTemplate() { |
||||
return db.db().collection('templates').findOne({ _id: 'a' }).then(res => res.data); |
||||
} |
||||
|
||||
exports.aaaaTemplate = () => { |
||||
return db.db.collection('templates').findOne({ _id: 'aaaa' }).then(res => res.data); |
||||
export function aaaaTemplate() { |
||||
return db.db().collection('templates').findOne({ _id: 'aaaa' }).then(res => res.data); |
||||
}; |
||||
|
||||
exports.soaTemplate = () => Object.seal(Object.freeze(Object.preventExtensions([ |
||||
export const soaTemplate = () => Object.seal(Object.freeze(Object.preventExtensions([ |
||||
{ |
||||
"ttl": 86400, |
||||
"ns": "ns1.basedns.net.", |
||||
"MBox": "root.basedflare.com.", |
||||
"refresh": 7200, |
||||
"retry": 3600, |
||||
"expire": 3600, |
||||
"minttl": 180, |
||||
"t": true |
||||
'ttl': 86400, |
||||
'ns': 'ns1.basedns.net.', |
||||
'MBox': 'root.basedflare.com.', |
||||
'refresh': 7200, |
||||
'retry': 3600, |
||||
'expire': 3600, |
||||
'minttl': 180, |
||||
't': true |
||||
}, |
||||
]))); |
||||
|
||||
exports.nsTemplate = () => Object.seal(Object.freeze(Object.preventExtensions([ |
||||
export const nsTemplate = () => Object.seal(Object.freeze(Object.preventExtensions([ |
||||
{ |
||||
"ttl": 86400, |
||||
"host": "ns1.basedns.net.", |
||||
"t": true |
||||
'ttl': 86400, |
||||
'host': 'ns1.basedns.net.', |
||||
't': true |
||||
}, |
||||
{ |
||||
"ttl": 86400, |
||||
"host": "ns2.basedns.cloud.", |
||||
"t": true |
||||
'ttl': 86400, |
||||
'host': 'ns2.basedns.cloud.', |
||||
't': true |
||||
}, |
||||
{ |
||||
"ttl": 86400, |
||||
"host": "ns3.basedns.services.", |
||||
"t": true |
||||
'ttl': 86400, |
||||
'host': 'ns3.basedns.services.', |
||||
't': true |
||||
} |
||||
]))); |
||||
|
Loading…
Reference in new issue