1
0
Fork 1
mirror of https://github.com/thatmattlove/hyperglass.git synced 2026-01-17 08:48:05 +00:00

fix import

This commit is contained in:
checktheroads 2020-01-17 02:50:57 -07:00
parent ca90cb4e10
commit 0728965999
285 changed files with 24331 additions and 9611 deletions

View file

@ -50,7 +50,6 @@ from hyperglass import configuration
from hyperglass import constants
from hyperglass import exceptions
from hyperglass import execution
from hyperglass import render
from hyperglass import util
stackprinter.set_excepthook()

View file

@ -5,14 +5,20 @@ import asyncio
from pathlib import Path
# Third Party Imports
import ujson as json
import yaml
from aiofile import AIOFile
from pydantic import ValidationError
# Project Imports
from hyperglass.configuration.markdown import get_markdown
from hyperglass.configuration.models import commands as _commands
from hyperglass.configuration.models import params as _params
from hyperglass.configuration.models import routers as _routers
from hyperglass.constants import CREDIT
from hyperglass.constants import DEFAULT_HELP
from hyperglass.constants import DEFAULT_DETAILS
from hyperglass.constants import DEFAULT_TERMS
from hyperglass.constants import LOG_HANDLER
from hyperglass.constants import LOG_HANDLER_FILE
from hyperglass.constants import LOG_LEVELS
@ -314,6 +320,60 @@ def _build_queries():
return queries
content_params = json.loads(
params.general.json(
include={"primary_asn", "org_name", "site_title", "site_description"}
)
)
def _build_vrf_help():
"""Build a dict of vrfs as keys, help content as values.
Returns:
{dict} -- Formatted VRF help
"""
all_help = {}
for vrf in devices.vrf_objects:
vrf_help = {}
for command in Supported.query_types:
cmd = getattr(vrf.info, command)
help_params = content_params
if cmd.params.title is None:
cmd.params.title = (
f"{vrf.display_name}: {getattr(params.branding.text, command)}"
)
help_params.update(cmd.params.dict())
md = asyncio.run(
get_markdown(
config_path=cmd,
default=DEFAULT_DETAILS[command],
params=help_params,
)
)
vrf_help.update(
{command: {"content": md, "enable": cmd.enable, "params": help_params}}
)
all_help.update({vrf.name: vrf_help})
return all_help
content_vrf = _build_vrf_help()
content_help = asyncio.run(
get_markdown(
config_path=params.branding.help_menu,
default=DEFAULT_HELP,
params=content_params,
)
)
content_terms = asyncio.run(
get_markdown(
config_path=params.branding.terms, default=DEFAULT_TERMS, params=content_params
)
)
content_credit = CREDIT
vrfs = _build_vrfs()
queries = _build_queries()
networks = _build_networks()
@ -346,6 +406,12 @@ _frontend_params.update(
"devices": frontend_devices,
"networks": networks,
"vrfs": vrfs,
"content": {
"help_menu": content_help,
"terms": content_terms,
"credit": content_credit,
"vrf": content_vrf,
},
}
)
frontend_params = _frontend_params

View file

@ -0,0 +1,63 @@
# Third Party Imports
from aiofile import AIOFile
# Project Imports
from hyperglass.util import log
async def _get_file(path_obj):
"""Read a file.
Arguments:
path_obj {Path} -- Path to file.
Returns:
{str} -- File contents
"""
async with AIOFile(path_obj, "r") as raw_file:
file = await raw_file.read()
return file
async def format_markdown(content, params):
"""Format content with config parameters.
Arguments:
content {str} -- Unformatted content
Returns:
{str} -- Formatted content
"""
try:
fmt = content.format(**params)
except KeyError:
fmt = content
return fmt
async def get_markdown(config_path, default, params):
"""Get markdown file if specified, or use default.
Format the content with config parameters.
Arguments:
config_path {object} -- content config
default {str} -- default content
Returns:
{str} -- Formatted content
"""
log.debug(f"Getting Markdown content for '{params['title']}'")
if config_path.enable and config_path.file is not None:
md = await _get_file(config_path.file)
else:
md = default
log.debug(f"Unformatted Content for '{params['title']}':\n{md}")
md_fmt = await format_markdown(md, params)
log.debug(f"Formatted Content for '{params['title']}':\n{md_fmt}")
return md_fmt

View file

@ -22,8 +22,6 @@ from hyperglass.configuration.models._utils import HyperglassModel
class Branding(HyperglassModel):
"""Validation model for params.branding."""
site_title: StrictStr = "hyperglass"
class Colors(HyperglassModel):
"""Validation model for params.colors."""
@ -103,11 +101,11 @@ class Branding(HyperglassModel):
logo_dark = values.get("dark")
default_logo_light = (
Path(__file__).parent.parent.parent
/ "static/ui/images/hyperglass-light.png"
/ "static/images/hyperglass-light.png"
)
default_logo_dark = (
Path(__file__).parent.parent.parent
/ "static/ui/images/hyperglass-dark.png"
/ "static/images/hyperglass-dark.png"
)
# Use light logo as dark logo if dark logo is undefined.

View file

@ -26,6 +26,7 @@ class General(HyperglassModel):
debug: StrictBool = False
primary_asn: StrictStr = "65001"
org_name: StrictStr = "The Company"
site_title: StrictStr = "hyperglass"
site_description: StrictStr = "{org_name} Network Looking Glass"
site_keywords: List[StrictStr] = [
"hyperglass",

View file

@ -32,7 +32,7 @@ class OpenGraph(HyperglassModel):
if value is None:
value = (
Path(__file__).parent.parent.parent
/ "static/ui/images/hyperglass-opengraph.png"
/ "static/images/hyperglass-opengraph.png"
)
with PilImage.open(value) as img:
width, height = img.size

View file

@ -12,19 +12,38 @@ from typing import Optional
# Third Party Imports
from pydantic import FilePath
from pydantic import IPvAnyNetwork
from pydantic import StrictBool
from pydantic import StrictStr
from pydantic import constr
from pydantic import validator
# Project Imports
from hyperglass.configuration.models._utils import HyperglassModel
from hyperglass.configuration.models._utils import HyperglassModelExtra
class InfoConfigParams(HyperglassModelExtra):
"""Validation model for per-help params."""
title: Optional[StrictStr]
class InfoConfig(HyperglassModel):
"""Validation model for help configuration."""
enable: StrictBool = True
file: Optional[FilePath]
params: InfoConfigParams = InfoConfigParams()
class Info(HyperglassModel):
"""Validation model for per-VRF help files."""
"""Validation model for per-VRF, per-Command help."""
bgp_aspath: Optional[FilePath]
bgp_community: Optional[FilePath]
bgp_aspath: InfoConfig = InfoConfig()
bgp_community: InfoConfig = InfoConfig()
bgp_route: InfoConfig = InfoConfig()
ping: InfoConfig = InfoConfig()
traceroute: InfoConfig = InfoConfig()
class DeviceVrf4(HyperglassModel):

View file

@ -25,24 +25,24 @@ LOG_HANDLER = {"sink": sys.stdout, "format": LOG_FMT, "level": "INFO"}
LOG_HANDLER_FILE = {"format": LOG_FMT, "level": "INFO"}
CREDIT = """
Powered by [**hyperglass**](https://github.com/checktheroads/hyperglass). Source code \
licensed \
[_BSD 3-Clause Clear_](https://github.com/checktheroads/hyperglass/blob/master/LICENSE).
"""
DEFAULT_TERMS = """
---
template: footer
---
By using {{ branding.site_name }}, you agree to be bound by the following terms of \
use: All queries executed on this page are logged for analysis and troubleshooting. \
By using {site_title}, you agree to be bound by the following terms of use:
All queries executed on this page are logged for analysis and troubleshooting. \
Users are prohibited from automating queries, or attempting to process queries in \
bulk. This service is provided on a best effort basis, and {{ general.org_name }} \
bulk. This service is provided on a best effort basis, and {org_name} \
makes no availability or performance warranties or guarantees whatsoever.
"""
DEFAULT_DETAILS = {
"bgp_aspath": r"""
---
template: bgp_aspath
title: Supported AS Path Patterns
---
{{ branding.site_name }} accepts the following `AS_PATH` regular expression patterns:
"bgp_aspath": """
{site_title} accepts the following `AS_PATH` regular expression patterns:
| Expression | Match |
| :------------------- | :-------------------------------------------- |
@ -53,17 +53,24 @@ title: Supported AS Path Patterns
| `_65000(_.+_)65001$` | Anything from 65001 that passed through 65000 |
""",
"bgp_community": """
---
template: bgp_community
title: BGP Communities
---
{{ branding.site_name }} makes use of the following BGP communities:
{site_title} makes use of the following BGP communities:
| Community | Description |
| :-------- | :---------- |
| `65000:1` | Example 1 |
| `65000:2` | Example 2 |
| `65000:3` | Example 3 |
""",
"bgp_route": """
Performs BGP table lookup based on IPv4/IPv6 prefix.
""",
"ping": """
Sends 5 ICMP echo requests to the target.
""",
"traceroute": """
Performs UDP Based traceroute to the target. \
For information about how to interpret traceroute results, [click here]\
(https://hyperglass.readthedocs.io/en/latest/assets/traceroute_nanog.pdf).
""",
}
@ -108,27 +115,37 @@ ets/traceroute_nanog.pdf" target="_blank">click here</a>.
DEFAULT_HELP = """
---
template: default_help
---
##### BGP Route
Performs BGP table lookup based on IPv4/IPv6 prefix.
<hr>
---
##### BGP Community
Performs BGP table lookup based on <a href="https://tools.ietf.org/html/rfc4360" target\
="_blank">Extended</a> or <a href="https://tools.ietf.org/html/rfc8195" target=\
"_blank">Large</a> community value.
<hr>
Performs BGP table lookup based on [Extended](https://tools.ietf.org/html/rfc4360) \
or [Large](https://tools.ietf.org/html/rfc8195) community value.
---
##### BGP AS Path
Performs BGP table lookup based on `AS_PATH` regular expression.
<hr>
---
##### Ping
Sends 5 ICMP echo requests to the target.
<hr>
---
##### Traceroute
Performs UDP Based traceroute to the target.<br>For information about how to \
interpret traceroute results, <a href="https://hyperglass.readthedocs.io/en/latest/ass\
ets/traceroute_nanog.pdf" target="_blank">click here</a>.
Performs UDP Based traceroute to the target.
For information about how to interpret traceroute results, [click here]\
(https://hyperglass.readthedocs.io/en/latest/assets/traceroute_nanog.pdf).
"""

View file

@ -9,6 +9,7 @@ from pathlib import Path
# Third Party Imports
import aredis
from aiofile import AIOFile
from prometheus_client import CONTENT_TYPE_LATEST
from prometheus_client import CollectorRegistry
from prometheus_client import Counter
@ -21,7 +22,8 @@ from sanic.exceptions import NotFound
from sanic.exceptions import ServerError
from sanic.exceptions import ServiceUnavailable
from sanic_limiter import Limiter
from sanic_limiter import RateLimitExceeded
# from sanic_limiter import RateLimitExceeded
from sanic_limiter import get_remote_address
# Project Imports
@ -37,7 +39,6 @@ from hyperglass.exceptions import RestError
from hyperglass.exceptions import ScrapeError
from hyperglass.execution.execute import Execute
from hyperglass.models.query import Query
from hyperglass.render import render_html
from hyperglass.util import check_python
from hyperglass.util import cpu_count
from hyperglass.util import log
@ -55,12 +56,22 @@ tempdir = tempfile.TemporaryDirectory(prefix="hyperglass_")
os.environ["prometheus_multiproc_dir"] = tempdir.name
# Static File Definitions
static_dir = Path(__file__).parent / "static" / "ui"
log.debug(f"Static Files: {static_dir}")
STATIC_DIR = Path(__file__).parent / "static"
UI_DIR = STATIC_DIR / "ui"
IMAGES_DIR = STATIC_DIR / "images"
NEXT_DIR = UI_DIR / "_next"
INDEX = UI_DIR / "index.html"
NOTFOUND = UI_DIR / "404.html"
NOFLASH = UI_DIR / "noflash.js"
log.debug(f"Static Files: {STATIC_DIR}")
# Main Sanic App Definition
app = Sanic(__name__)
app.static("/ui", str(static_dir))
app.static("/ui", str(UI_DIR))
app.static("/_next", str(NEXT_DIR))
app.static("/images", str(IMAGES_DIR))
app.static("/ui/images", str(IMAGES_DIR))
app.static("/noflash.js", str(NOFLASH))
log.debug(app.config)
# Sanic Web Server Parameters
@ -228,31 +239,35 @@ async def handle_backend_errors(request, exception):
async def handle_404(request, exception):
"""Render full error page for invalid URI."""
path = request.path
html = render_html("404", uri=path)
# html = render_html("404", uri=path)
client_addr = get_remote_address(request)
count_notfound.labels(exception, path, client_addr).inc()
log.error(f"Error: {exception}, Path: {path}, Source: {client_addr}")
return sanic_response.html(html, status=404)
# return sanic_response.html(html, status=404)
async with AIOFile(NOTFOUND, "r") as nf:
html = await nf.read()
return sanic_response.html(html)
@app.exception(RateLimitExceeded)
async def handle_429(request, exception):
"""Render full error page for too many site queries."""
html = render_html("ratelimit-site")
client_addr = get_remote_address(request)
count_ratelimit.labels(exception, client_addr).inc()
log.error(f"Error: {exception}, Source: {client_addr}")
return sanic_response.html(html, status=429)
# @app.exception(RateLimitExceeded)
# async def handle_429(request, exception):
# """Render full error page for too many site queries."""
# html = render_html("ratelimit-site")
# client_addr = get_remote_address(request)
# count_ratelimit.labels(exception, client_addr).inc()
# log.error(f"Error: {exception}, Source: {client_addr}")
# return sanic_response.html(html, status=429)
@app.exception(ServerError)
async def handle_500(request, exception):
"""Render general error page."""
client_addr = get_remote_address(request)
count_errors.labels(500, exception, client_addr, None, None, None).inc()
log.error(f"Error: {exception}, Source: {client_addr}")
html = render_html("500")
return sanic_response.html(html, status=500)
# @app.exception(ServerError)
# async def handle_500(request, exception):
# """Render general error page."""
# client_addr = get_remote_address(request)
# count_errors.labels(500, exception, client_addr, None, None, None).inc()
# log.error(f"Error: {exception}, Source: {client_addr}")
# html = render_html("500")
# return sanic_response.html(html, status=500)
async def clear_cache():
@ -269,8 +284,12 @@ async def clear_cache():
@limiter.limit(rate_limit_site, error_message="Site")
async def site(request):
"""Serve main application front end."""
html = await render_html("form", primary_asn=params.general.primary_asn)
return sanic_response.html(html)
# html = await render_html("form", primary_asn=params.general.primary_asn)
# return sanic_response.html(html)
async with AIOFile(INDEX, "r") as entry:
html = await entry.read()
return sanic_response.html(html)
@app.route("/config", methods=["GET", "OPTIONS"])
@ -326,7 +345,7 @@ async def hyperglass_main(request):
# Use hashed query_data string as key for for k/v cache store so
# each command output value is unique.
cache_key = hash(query_data)
cache_key = hash(str(query_data))
# Define cache entry expiry time
cache_timeout = params.features.cache.timeout

View file

@ -1,6 +0,0 @@
"""Renders Jinja2 & Sass templates for use by the front end application."""
# Project Imports
# flake8: noqa: F401
from hyperglass.render.html import render_html
from hyperglass.render.webassets import render_assets

View file

@ -1,254 +0,0 @@
"""Renders Jinja2 & Sass templates for use by the front end application."""
# Standard Library Imports
from pathlib import Path
# Third Party Imports
import jinja2
import yaml
from aiofile import AIOFile
from markdown2 import Markdown
# Project Imports
from hyperglass.configuration import devices
from hyperglass.configuration import networks
from hyperglass.configuration import params
from hyperglass.constants import DEFAULT_DETAILS
from hyperglass.constants import DEFAULT_HELP
from hyperglass.constants import DEFAULT_TERMS
from hyperglass.exceptions import ConfigError
from hyperglass.exceptions import HyperglassError
from hyperglass.util import log
# Module Directories
WORKING_DIR = Path(__file__).resolve().parent
JINJA_LOADER = jinja2.FileSystemLoader(str(WORKING_DIR))
JINJA_ENV = jinja2.Environment(
loader=JINJA_LOADER,
autoescape=True,
extensions=["jinja2.ext.autoescape"],
enable_async=True,
)
_MD_CONFIG = {
"extras": {
"break-on-newline": True,
"code-friendly": True,
"tables": True,
"html-classes": {"table": "table"},
}
}
MARKDOWN = Markdown(**_MD_CONFIG)
async def parse_md(raw_file):
file_list = raw_file.split("---", 2)
file_list_len = len(file_list)
if file_list_len == 1:
fm = {}
content = file_list[0]
elif file_list_len == 3 and file_list[1].strip():
try:
fm = yaml.safe_load(file_list[1])
except yaml.YAMLError as ye:
raise ConfigError(str(ye)) from None
content = file_list[2]
else:
fm = {}
content = ""
return (fm, content)
async def get_file(path_obj):
async with AIOFile(path_obj, "r") as raw_file:
file = await raw_file.read()
return file
async def render_help():
if params.branding.help_menu.file is not None:
help_file = await get_file(params.branding.help_menu.file)
else:
help_file = DEFAULT_HELP
fm, content = await parse_md(help_file)
content_template = JINJA_ENV.from_string(content)
content_rendered = await content_template.render_async(params, info=fm)
return {"content": MARKDOWN.convert(content_rendered), **fm}
async def render_terms():
if params.branding.terms.file is not None:
terms_file = await get_file(params.branding.terms.file)
else:
terms_file = DEFAULT_TERMS
fm, content = await parse_md(terms_file)
content_template = JINJA_ENV.from_string(content)
content_rendered = await content_template.render_async(params, info=fm)
return {"content": MARKDOWN.convert(content_rendered), **fm}
async def render_details():
details = []
for vrf in devices.vrf_objects:
detail = {"name": vrf.name, "display_name": vrf.display_name}
info_attrs = ("bgp_aspath", "bgp_community")
command_info = []
for attr in info_attrs:
file = getattr(vrf.info, attr)
if file is not None:
raw_content = await get_file(file)
fm, content = await parse_md(raw_content)
else:
fm, content = await parse_md(DEFAULT_DETAILS[attr])
content_template = JINJA_ENV.from_string(content)
content_rendered = await content_template.render_async(params, info=fm)
content_html = MARKDOWN.convert(content_rendered)
command_info.append(
{
"id": f"{vrf.name}-{attr}",
"name": attr,
"frontmatter": fm,
"content": content_html,
}
)
detail.update({"commands": command_info})
details.append(detail)
return details
async def render_html(template_name, **kwargs):
"""Render Jinja2 HTML templates.
Arguments:
template_name {str} -- Jinja2 template name
Raises:
HyperglassError: Raised if template is not found
Returns:
{str} -- Rendered template
"""
try:
template_file = f"templates/{template_name}.html.j2"
template = JINJA_ENV.get_template(template_file)
except jinja2.TemplateNotFound as template_error:
log.error(
f"Error rendering Jinja2 template {str(Path(template_file).resolve())}."
)
raise HyperglassError(template_error)
rendered_help = await render_help()
rendered_terms = await render_terms()
rendered_details = await render_details()
sub_templates = {
"details": rendered_details,
"help": rendered_help,
"terms": rendered_terms,
"networks": networks,
**kwargs,
}
return await template.render_async(params, **sub_templates)
# async def generate_markdown(section, file_name=None):
# """Render markdown as HTML.
# Arguments:
# section {str} -- Section name
# Keyword Arguments:
# file_name {str} -- Markdown file name (default: {None})
# Raises:
# HyperglassError: Raised if YAML front matter is unreadable
# Returns:
# {dict} -- Frontmatter dictionary
# """
# if section == "help" and params.branding.help_menu.file is not None:
# info = await get_file(params.branding.help_menu.file)
# elif section == "help" and params.branding.help_menu.file is None:
# info = DEFAULT_HELP
# elif section == "details":
# file = WORKING_DIR.joinpath(f"templates/info/details/{file_name}.md")
# if file.exists():
# with file.open(mode="r") as file_raw:
# yaml_raw = file_raw.read()
# else:
# yaml_raw = DEFAULT_DETAILS[file_name]
# _, frontmatter, content = yaml_raw.split("---", 2)
# md_config = {
# "extras": {
# "break-on-newline": True,
# "code-friendly": True,
# "tables": True,
# "html-classes": {"table": "table"},
# }
# }
# markdown = Markdown(**md_config)
# frontmatter_rendered = JINJA_ENV.from_string(frontmatter).render(params)
# if frontmatter_rendered:
# frontmatter_loaded = yaml.safe_load(frontmatter_rendered)
# elif not frontmatter_rendered:
# frontmatter_loaded = {"frontmatter": None}
# content_rendered = await JINJA_ENV.from_string(content).render_async(
# params, info=frontmatter_loaded
# )
# help_dict = dict(content=markdown.convert(content_rendered), **frontmatter_loaded)
# if not help_dict:
# raise HyperglassError(f"Error reading YAML frontmatter for {file_name}")
# return help_dict
# async def render_html(template_name, **kwargs):
# """Render Jinja2 HTML templates.
# Arguments:
# template_name {str} -- Jinja2 template name
# Raises:
# HyperglassError: Raised if template is not found
# Returns:
# {str} -- Rendered template
# """
# detail_items = ("footer", "bgp_aspath", "bgp_community")
# details = {}
# for details_name in detail_items:
# details_data = await generate_markdown("details", details_name)
# details.update({details_name: details_data})
# rendered_help = await generate_markdown("help")
# try:
# template_file = f"templates/{template_name}.html.j2"
# template = JINJA_ENV.get_template(template_file)
# except jinja2.TemplateNotFound as template_error:
# log.error(f"Error rendering Jinja2 template {Path(template_file).resolve()}.")
# raise HyperglassError(template_error)
# return await template.render_async(
# params,
# rendered_help=rendered_help,
# details=details,
# networks=networks,
# **kwargs,
# )

View file

@ -1,2 +0,0 @@
.DS_Store
old/

View file

@ -1,10 +0,0 @@
{% extends "templates/base.html.j2" %}
{% block content %}
{% import "templates/errortext.html.j2" as errortext %}
{{ errortext.errortext(branding.text.error404.title, branding.text.error404.subtitle.format(uri=uri), branding.text.error404.button) }}
{% endblock %}
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -1,10 +0,0 @@
{% extends "templates/base.html.j2" %}
{% block content %}
{% import "templates/errortext.html.j2" as errortext %}
{{ errortext.errortext(branding.text.error500.title, branding.text.error500.subtitle, branding.text.error500.button) }}
{% endblock %}
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -1,56 +0,0 @@
<!DOCTYPE html>
<html class="h-100">
<head>
<title>{{ branding.site_title }}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="apple-touch-icon" sizes="180x180" href="{{ branding.logo.favicons }}apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="16x16" href="{{ branding.logo.favicons }}favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="{{ branding.logo.favicons }}favicon-32x32.png" />
<link rel="manifest" href="{{ branding.logo.favicons }}site.webmanifest" />
<link rel="mask-icon" href="{{ branding.logo.favicons }}safari-pinned-tab.svg"
color="{{ branding.colors.primary }}" />
<link rel="shortcut icon" href="{{ branding.logo.favicons }}favicon.ico" />
<meta name="msapplication-TileColor" content="{{ branding.colors.primary }}" />
<meta name="msapplication-config" content="{{ branding.logo.favicons }}browserconfig.xml" />
<meta name="theme-color" content="{{ branding.colors.button_submit }}" />
<link href="ui/hyperglass.css" rel="stylesheet" type="text/css" />
{% block head %}
{% endblock %}
</head>
<body class="d-flex flex-column h-100">
{% include "templates/ratelimit-query.html.j2" %}
{% include "templates/info.html.j2" %}
<div class="container-fluid d-flex w-100 h-100 mx-auto flex-column" id="hg-page-container">
<main role="main" class="flex-shrink-0">
{% block content %}
{% endblock %}
</main>
<button class="animsition btn btn-link text-primary hg-back d-none" id="hg-back-btn" data-animsition-out-class="fade-out-left" data-animsition-in-class="fade-in-left">
<i class="remixicon-arrow-left-s-line"></i>
</button>
{% include "templates/footer.html.j2" %}
</div>
<script src="ui/hyperglass.js"></script>
{% if general.google_analytics %}
<!--Google Analytics-->
<script async src="https://www.googletagmanager.com/gtag/js?id={{general.google_analytics}}">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "{{ general.google_analytics }}");
</script>
{% endif %}
{% block scripts %}
{% endblock %}
</body>
</html>

View file

@ -1,11 +0,0 @@
{% macro errortext(title, subtitle, button) -%}
<div class="container-fluid d-flex w-100 h-100 p-3 mx-auto flex-column">
<div class="jumbotron bg-danger">
<h1 class="display-4">{{ title }}</h1>
<p class="lead">{{ subtitle }}</p>
<hr class="my-4">
<a class="btn btn-outline-danger btn-lg" href="/" role="button">{{ button }}</a>
</div>
</div>
{%- endmacro %}

View file

@ -1,39 +0,0 @@
<nav class="navbar fixed-bottom navbar-footer bg-overlay pt-0 pb-0">
<div class="container-fluid row no-gutters text-center p-0">
{% if branding.terms.enable %}
<div class="col-auto float-left">
<div class="d-none" id="hg-footer-terms-html">{{ details.footer.content | safe }}</div>
<a class="btn btn-link" id="hg-footer-terms-btn" href="#" data-toggle="popover" tabindex="0" role="button"
title="{{ branding.text.terms }}"><small>{{ branding.text.terms }}</small></a>
</div>
{% endif %}
{% if branding.help_menu.enable %}
<div class="col-auto float-left">
<div class="d-none" id="hg-footer-help-html">{{ rendered_help.content | safe }}</div>
<a class="btn btn-link" id="hg-footer-help-btn" href="#" data-toggle="popover" tabindex="0" role="button"
title="{{ branding.text.info }}"><small>{{ branding.text.info }}</small></a>
</div>
{% endif %}
<div class="col-auto mr-auto"></div>
{% if branding.credit.enable %}
<div class="col-auto float-right">
<div class="d-none" id="hg-footer-credit-title">
Powered by <a href="https://github.com/checktheroads/hyperglass" target="_blank">hyperglass</a>.
</div>
<div class="d-none" id="hg-footer-credit-content">
Source code licensed <a href="https://github.com/checktheroads/hyperglass/blob/master/LICENSE"
target="_blank">BSD 3-Clause Clear.</a>
</div>
<a class="btn btn-link" id="hg-footer-credit-btn" href="#" data-toggle="popover" tabindex="0" role="button"><small><i
class="remixicon-code-s-slash-line"></i></small></a>
</div>
{% endif %}
{% if branding.peering_db.enable %}
<div class="col-auto float-right">
<a class="btn btn-link" href="https://as{{ general.primary_asn }}.peeringdb.com/"
target="_blank"><small>PeeringDB&nbsp;<i class="remixicon-share-circle-line"></i></small>
</a>
</div>
{% endif %}
</div>
</nav>

View file

@ -1,75 +0,0 @@
{% extends "templates/base.html.j2" %}
{% block content %}
<div id="hg-parent" class="mx-auto">
<div class="container animsition mw-lg-75 mw-xl-50" data-animsition-out-class="fade-out-right"
data-animsition-in-class="fade-in-left" id="hg-form">
<div class="container-fluid d-flex justify-content-center mb-4 text-center">
{% import "templates/title.html.j2" as title %}
{{ title.title(branding, primary_asn, size_title="h1", size_subtitle="h4") }}
</div>
<form class="needs-validation" onsubmit="return false" name="queryform" id="lgForm" action="?" method="POST"
novalidate>
<div class="form-row mb-4">
<div class="col-md col-sm-12 mb-4 mb-md-0">
<select multiple class="form-control form-control-lg hg-select" id="location" data-live-search="true"
title="{{ branding.text.query_location }}">
{% for (netname, loc_params) in networks.items() %}
<optgroup label="{{ netname }}">
{% for param in loc_params %}
<option id='{{ param.hostname }}' value='{{ param.hostname }}'
data-tokens='{{ netname }} {{param.hostname}}' data-display-name="{{ param.display_name }}"
data-netname="{{ netname }}">
{{ param.display_name }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
<div class="col-md col-sm-12">
<select class="form-control form-control-lg hg-select custom-select-lg" id="query_type"
title="{{ branding.text.query_type }}" required>
{% if features.bgp_route.enable %}
<option id="bgp_route" value="bgp_route" data-display-name="{{ branding.text.bgp_route }}">
{{ branding.text.bgp_route }}</option>
{% endif %}
{% if features.bgp_community.enable %}
<option id="bgp_community" value="bgp_community" data-display-name="{{ branding.text.bgp_community }}">
{{ branding.text.bgp_community }}</option>
{% endif %}
{% if features.bgp_aspath.enable %}
<option id="bgp_aspath" value="bgp_aspath" data-display-name="{{ branding.text.bgp_aspath }}">
{{ branding.text.bgp_aspath }}</option>
{% endif %}
{% if features.ping.enable %}
<option id="ping" value="ping" data-display-name="{{ branding.text.ping }}">{{ branding.text.ping }}
</option>
{% endif %}
{% if features.traceroute.enable %}
<option id="traceroute" value="traceroute" data-display-name="{{ branding.text.traceroute }}">
{{ branding.text.traceroute }}</option>
{% endif %}
</select>
</div>
</div>
<div class="form-row mb-4" id="hg-row-2">
<div id="hg-container-vrf"></div>
<div class="col" id="hg-container-target">
<div class="input-group input-group-lg">
<input class="form-control" type="text" placeholder="{{ branding.text.query_target }}"
aria-label="{{ branding.text.query_target }}" aria-describedby="query_target" id="query_target" required>
<div class="input-group-append" id="hg-target-append">
<button class="btn btn-primary" id="hg-submit-button" type="submit">
<div id="hg-submit-icon">
<i class="remixicon-search-line"></i>
</div>
</button>
</div>
</div>
</div>
</div>
</form>
</div>
{% include "templates/results.html.j2" %}
</div>
{% endblock %}

View file

@ -1,30 +0,0 @@
<div class="modal fade" id="hg-info-bgp_aspath" tabindex="-1" role="dialog" aria-labelledby="hg-info-title-bgp_aspath" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="hg-info-title-bgp_aspath">{{ details.bgp_aspath.title | safe }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{ details.bgp_aspath.content | safe }}
</div>
</div>
</div>
</div>
<div class="modal fade" id="hg-info-bgp_community" tabindex="-1" role="dialog" aria-labelledby="hg-info-title-bgp_community" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="hg-info-title-bgp_community">{{ details.bgp_community.title | safe }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{ details.bgp_community.content | safe }}
</div>
</div>
</div>
</div>

View file

@ -1,2 +0,0 @@
.DS_Store
*.md

View file

@ -1,7 +0,0 @@
---
template: bgp_aspath
link: <a href="#" id="help_link_bgpa">Supported BGP AS Path Expressions</a>
---
Performs BGP table lookup based on `AS_PATH` regular expression.
{{ info["bgp_aspath"]["link"] }}

View file

@ -1,7 +0,0 @@
---
template: bgp_community
link: <a href="#" onclick="help_link_bgpc">{{ general["org_name"] }} BGP Communities</a>
---
Performs BGP table lookup based on [Extended](https://tools.ietf.org/html/rfc4360) or [Large](https://tools.ietf.org/html/rfc8195) community value.
{{ info["bgp_community"]["link"] }}

View file

@ -1,4 +0,0 @@
---
template: bgp_route
---
Performs BGP table lookup based on IPv4/IPv6 prefix.

View file

@ -1,2 +0,0 @@
.DS_Store
*.md

View file

@ -1,12 +0,0 @@
+++
title = "Supported AS Path Patterns"
+++
{{ branding.site_name }} accepts the following `AS_PATH` regular expression patterns:
| Expression | Match |
| :----------------------- | ----------------------------------------------------: |
| `_65000$` | Originated by AS65000 |
| `^65000\_` | Received from AS65000 |
| `_65000_` | Via AS65000 |
| `_65000_65001_` | Via AS65000 and AS65001 |
| `_65000(_.+_)65001$` | Anything from AS65001 that passed through AS65000 |

View file

@ -1,10 +0,0 @@
+++
title = "BGP Communities"
+++
{{ branding.site_name }} makes use of the following BGP communities:
| Community | Description |
| :-------- | :---------- |
| `65000:1` | Example 1 |
| `65000:2` | Example 2 |
| `65000:3` | Example 3 |

View file

@ -1,3 +0,0 @@
+++
+++
By using {{ branding.site_name }}, you agree to be bound by the following terms of use: All queries executed on this page are logged for analysis and troubleshooting. Users are prohibited from automating queries, or attempting to process queries in bulk. This service is provided on a best effort basis, and {{ general.org_name }} makes no availability or performance warranties or guarantees whatsoever.

View file

@ -1,4 +0,0 @@
---
template: ping
---
Sends 5 ICMP echo requests to the target.

View file

@ -1,4 +0,0 @@
---
template: traceroute
---
Performs UDP Based traceroute to the target.<br>For information about how to interpret traceroute results, [click here](https://hyperglass.readthedocs.io/nanog_traceroute.pdf).

View file

@ -1,16 +0,0 @@
<div class="modal fade" tabindex="-1" role="dialog" id="hg-ratelimit-query">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content bg-danger">
<div class="modal-body">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h2 class="modal-title">{{ features.rate_limit.query.title }}</h2>
<p>{{ features.rate_limit.query.message }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-danger" data-dismiss="modal">{{ features.rate_limit.query.button }}</button>
</div>
</div>
</div>
</div>

View file

@ -1,10 +0,0 @@
{% extends "templates/base.html.j2" %}
{% block content %}
{% import "templates/errortext.html.j2" as errortext %}
{{ errortext.errortext(features.rate_limit.site.title, features.rate_limit.site.subtitle, features.rate_limit.site.button) }}
{% endblock %}
{% block footer %}
{% include "templates/footer.html.j2" %}
{% endblock %}

View file

@ -1,19 +0,0 @@
<div class="container animsition" data-animsition-out-class="fade-out-left" data-animsition-in-class="fade-in-right"
id="hg-results">
<div class="row align-items-center mb-md-1 mb-lg-3 mb-xl-3 px-0 py-3 py-md-1 mx-0 mw-100 mw-md-none">
<div class="d-flex col-12 col-lg-9 justify-content-center justify-content-lg-start text-center text-lg-left pl-lg-1"
data-href="/" id="hg-title-col">
{% import "templates/title.html.j2" as title %}
{{ title.title(branding, primary_asn, size_title="h1", size_subtitle="h4", direction="left") }}
</div>
<div id="hg-results-title"
class="d-flex col-12 col-lg-3 mb-0 mt-md-3 mb-md-2 px-1 px-md-0">
</div>
</div>
<div class="row mt-lg-1 mx-0 mw-100 mw-md-none">
<div class="col align-self-center">
<div class="accordion" id="hg-accordion">
</div>
</div>
</div>
</div>

View file

@ -1,140 +0,0 @@
// Jinja2-rendered theme elements
$hg-primary: {{ colors.primary }}
$hg-secondary: {{ colors.secondary }}
$hg-danger: {{ colors.danger }}
$hg-warning: {{ colors.warning }}
$hg-background: {{ colors.background }}
$hg-font-primary: {{ font.primary.name }}
$hg-font-primary-size: {{ font.primary.size }}
$hg-font-mono: {{ font.mono.name }}
$hg-font-mono-size: {{ font.mono.size }}
// Functions
@function findTextColor($color)
$inverted: invert($color)
@if (lightness($color) > 55%)
$text-color: scale-color($inverted, $lightness: -90%)
@return grayscale($text-color)
@else
$text-color: scale-color($inverted, $lightness: 90%)
@return grayscale($text-color)
@function findOverlayColor($color)
@if (lightness($color) > 55%)
$darker-color: scale-color($color, $lightness: -3%)
@return grayscale($darker-color)
@else
$darker-color: scale-color($color, $lightness: 5%)
@return grayscale($darker-color)
@function findSubtleColor($color)
@if (lightness($color) > 55%)
$subtle-color: scale-color($color, $lightness: -3%)
@return grayscale($subtle-color)
@else
$subtle-color: scale-color($color, $lightness: 20%)
@return grayscale($subtle-color)
@function findInvertedColor($color)
$inverted: invert($color)
@if (lightness($inverted) > 55%)
$border: scale-color($inverted, $lightness: -3%)
@return grayscale($border)
@else
$border: scale-color($inverted, $lightness: 3%)
@return grayscale($border)
@function findFieldColor($color)
@if (lightness($color) > 55%)
@return $color
@else
$darker-color: scale-color($color, $lightness: 5%)
@return grayscale($darker-color)
@function findSubtleText($color)
@if (lightness($color) > 55%)
$subtle-color: scale-color($color, $lightness: -20%)
@return grayscale($subtle-color)
@else
$subtle-color: scale-color($color, $lightness: 40%)
@return grayscale($subtle-color)
// Body Color
$body-bg: $hg-background
$body-color: findTextColor($body-bg)
// Global variables
$hg-overlay: findOverlayColor($hg-background)
$hg-inverted: findInvertedColor($hg-background)
$hg-field: findFieldColor($hg-background)
$hg-border: rgba(findInvertedColor($hg-overlay), .2)
$hg-primary-color: findTextColor($hg-primary)
// App variables
$hg-loading: $hg-overlay
$hg-footer: $hg-overlay
$hg-overlay-bg: $hg-overlay
$hg-overlay-color: findTextColor($hg-overlay-bg)
$hg-inverted-bg: $hg-inverted
$hg-inverted-color: findTextColor($hg-inverted-bg)
$hg-field-bg: $hg-field
$hg-field-color: findTextColor($hg-field-bg)
// Bootstrap Overrides
//// Components
$component-active-color: findTextColor($hg-primary)
//// Flags
$enable-responsive-font-sizes: true
$enable-validation-icons: false
$theme-colors: ("primary": $hg-primary, "secondary": $hg-secondary, "danger": $hg-danger, "warning": $hg-warning, "light": findTextColor(black), "dark": findTextColor(white), "overlay": $hg-overlay)
//// Fonts/Text
$font-family-sans-serif: $hg-font-primary, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"
$font-family-monospace: $hg-font-mono, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace
$headings-font-weight: 400
$font-size-base: $hg-font-primary-size
//// Borders
$border-radius: .5rem
$border-radius-lg: .7rem
$border-radius-sm: .4rem
//// Popovers
$popover-bg: $hg-field-bg
$popover-border-color: $hg-border
$popover-body-color: $hg-field-color
//// Cards
$card-bg: $hg-field-bg
$card-color: $hg-field-color
$card-border-color: $hg-border
//// Dropdowns
$dropdown-bg: $hg-field-bg
$dropdown-color: $hg-field-color
$dropdown-divider-bg: $hg-border
$dropdown-border-color: $hg-border
$dropdown-header-color: findSubtleText($dropdown-color)
$dropdown-link-color: $hg-field-color
$dropdown-link-hover-bg: $hg-primary
$dropdown-link-hover-color: $hg-primary-color
$dropdown-link-active-bg: $hg-primary
$dropdown-link-active-color: $hg-primary-color
//// Forms
$input-bg: $hg-field-bg
$input-color: $hg-field-color
$input-border-color: $hg-border
$input-placeholder-color: findSubtleText($hg-field-color)
$input-btn-focus-box-shadow: none
$input-focus-bg: findSubtleColor($input-bg)
$input-focus-color: $input-color
$input-focus-border-color: findOverlayColor($hg-border)
//// Code
$code-color: $hg-secondary
$kbd-bg: findOverlayColor($hg-overlay)
$kbd-color: $hg-overlay-color
$pre-color: $body-color

View file

@ -1,26 +0,0 @@
{% macro title(branding, primary_asn, size_title="h1", size_subtitle="h4", direction="none") -%}
{% if branding.text.title_mode == 'text_only' %}
<div class="col p-0">
<p class="{{ size_title }}">{{ branding.text.title }}</p>
<p class="{{ size_subtitle }}">{{ branding.text.subtitle.format(primary_asn=primary_asn) }}</p>
</div>
{% elif branding.text.title_mode == 'all' %}
<img src="{{ branding.logo.logo_path }}" style="width: {{ branding.logo.width }}px; {% if branding.logo.height %} height: {{ branding.logo.height }}px;{% endif %}">
<p class="{{ size_title }}">{{ branding.text.title }}<p>
<p class="{{ size_subtitle }}">{{ branding.text.subtitle.format(primary_asn=primary_asn) }}</p>
{% elif branding.text.title_mode == 'logo_title' %}
<div class="float-{{ direction }} mw-sm-100 mw-md-75 mw-lg-50 mw-xl-50">
<img class="img-fluid hg-logo" src="{{ branding.logo.logo_path }}" style="width: {{ branding.logo.width }}px; {% if branding.logo.height %} height: {{ branding.logo.height }}px;{% endif %}">
</div>
<p clas="{{ size_title }}">{{ branding.text.title }}</p>
{% elif branding.text.title_mode == 'logo_only' %}
<div class="float-{{ direction }} mw-sm-100 mw-md-75 mw-lg-50 mw-xl-50">
<img class="img-fluid hg-logo" src="{{ branding.logo.logo_path }}" style="width: {{ branding.logo.width }}px; {% if branding.logo.height %} height: {{ branding.logo.height }}px;{% endif %}">
</div>
{% endif %}
{%- endmacro %}

View file

@ -1,166 +0,0 @@
"""Renders Jinja2 & Sass templates for use by the front end application."""
# Standard Library Imports
import asyncio
import json
import time
from pathlib import Path
# Third Party Imports
import jinja2
from aiofile import AIOFile
# Project Imports
from hyperglass.configuration import frontend_devices
from hyperglass.configuration import frontend_networks
from hyperglass.configuration import frontend_params
from hyperglass.configuration import params
from hyperglass.exceptions import HyperglassError
from hyperglass.util import log
# File & Path Definitions
CWD = Path(__file__).parent
ROOT_DIR = CWD.parent
FRONTEND_CONFIG = ROOT_DIR / "static/src/js/frontend.json"
FONT_DIR = ROOT_DIR / "static/src/sass/fonts"
FONT_CMD = ROOT_DIR / "static/src/node_modules/get-google-fonts/cli.js"
THEME_FILE = ROOT_DIR / "static/src/sass/theme.sass"
STATIC_DIR = ROOT_DIR / "static/src"
# Jinja2 Config
JINJA_FILE_LOADER = jinja2.FileSystemLoader(str(CWD))
JINJA_ENV = jinja2.Environment(
loader=JINJA_FILE_LOADER,
autoescape=True,
extensions=["jinja2.ext.autoescape"],
enable_async=True,
)
async def _render_frontend_config():
"""Render user config to JSON for use by frontend."""
log.debug("Rendering front end config...")
try:
async with AIOFile(FRONTEND_CONFIG, "w+") as file:
await file.write(
json.dumps(
{
"config": frontend_params,
"networks": frontend_networks,
"devices": frontend_devices,
}
)
)
await file.fsync()
log.debug("Rendered front end config")
except Exception as e:
raise HyperglassError(f"Error rendering front end config: {str(e)}")
async def _get_fonts():
"""Download Google fonts."""
log.debug("Downloading theme fonts...")
font_base = "https://fonts.googleapis.com/css?family={p}|{m}&display=swap"
font_primary = "+".join(params.branding.font.primary.name.split(" ")).strip()
font_mono = "+".join(params.branding.font.mono.name.split(" ")).strip()
font_url = font_base.format(p=font_primary + ":300,400,700", m=font_mono + ":400")
font_command = f"node {str(FONT_CMD)} -w -i '{font_url}' -o {str(FONT_DIR)}"
try:
proc = await asyncio.create_subprocess_shell(
cmd=font_command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=STATIC_DIR,
)
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=60)
for line in stdout.decode().strip().split("\n"):
log.debug(line)
if proc.returncode != 0:
output_error = stderr.decode("utf-8")
log.error(output_error)
raise RuntimeError(f"Error downloading font from URL {font_url}")
await proc.wait()
log.debug("Downloaded theme fonts")
except Exception as e:
raise HyperglassError(str(e))
async def _render_theme():
"""Render Jinja2 template to Sass file."""
log.debug("Rendering theme elements...")
try:
template = JINJA_ENV.get_template("templates/theme.sass.j2")
rendered_theme = await template.render_async(params.branding)
log.debug(f"Branding variables:\n{params.branding.json(indent=4)}")
log.debug(f"Rendered theme:\n{str(rendered_theme)}")
async with AIOFile(THEME_FILE, "w+") as file:
await file.write(rendered_theme)
await file.fsync()
log.debug("Rendered theme elements")
except Exception as e:
raise HyperglassError(f"Error rendering theme: {e}")
async def _build_assets():
"""Build, bundle, and minify Sass/CSS/JS web assets."""
log.debug("Building web assets...")
yarn_command = "yarn --silent --emoji false --json --no-progress build"
try:
proc = await asyncio.create_subprocess_shell(
cmd=yarn_command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=STATIC_DIR,
)
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=60)
output_out = json.loads(stdout.decode("utf-8").split("\n")[0])
if proc.returncode != 0:
output_error = json.loads(stderr.decode("utf-8").strip("\n"))
raise RuntimeError(
f'Error building web assets with script {output_out["data"]}:'
f'{output_error["data"]}'
)
await proc.wait()
log.debug(f'Built web assets with script {output_out["data"]}')
except Exception as e:
raise HyperglassError(str(e))
async def _render_all():
"""Run all asset rendering/building functions.
Raises:
HyperglassError: Raised if any downstream errors occur.
"""
try:
await _render_frontend_config()
await _get_fonts()
await _render_theme()
await _build_assets()
except HyperglassError as e:
raise HyperglassError(str(e)) from None
def render_assets():
"""Render assets."""
start = time.time()
asyncio.run(_render_all())
end = time.time()
elapsed = round(end - start, 2)
log.debug(f"Rendered assets in {elapsed} seconds.")

View file

@ -10,4 +10,5 @@ frontend.json
# NPM modules
node_modules/
# Downloaded Google Fonts
fonts/
fonts/
ui/*

View file

Before

Width:  |  Height:  |  Size: 577 B

After

Width:  |  Height:  |  Size: 577 B

View file

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

Before

Width:  |  Height:  |  Size: 849 B

After

Width:  |  Height:  |  Size: 849 B

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 253 KiB

After

Width:  |  Height:  |  Size: 253 KiB

View file

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

View file

@ -1,4 +0,0 @@
@import './sass/hyperglass.scss';
@import './node_modules/animsition/dist/css/animsition.min.css';
@import './node_modules/remixicon/fonts/remixicon.css';
@import './node_modules/bootstrap-select/dist/css/bootstrap-select.min.css';

View file

@ -1,8 +0,0 @@
// 3rd Party Libraries
const Popper = require('popper.js');
const bootstrap = require('bootstrap');
const selectpicker = require('bootstrap-select');
const animsition = require('animsition');
// hyperglass
const hyperglass = require('./js/hyperglass.es6');

View file

@ -1,12 +0,0 @@
env:
browser: true
es6: true
extends:
- airbnb-base
globals:
Atomics: readonly
SharedArrayBuffer: readonly
parserOptions:
ecmaVersion: 2018
sourceType: module
rules: {}

View file

@ -1,13 +0,0 @@
.DS_Store
# dev/test files
*.tmp*
test*
*.log
# generated theme file from hyperglass/hyperglass/render/templates/theme.sass.j2
theme.sass
# generated JSON file from ingested & validated YAML config
frontend.json
# NPM modules
node_modules/
# Downloaded Google Fonts
fonts/

View file

@ -1,133 +0,0 @@
function footerPopoverTemplate() {
const element = `
<div class="popover mw-sm-75 mw-md-50 mw-lg-25" role="tooltip">
<div class="arrow"></div>
<h3 class="popover-header"></h3>
<div class="popover-body"></div>
</div>`;
return element;
}
function feedbackInvalid(msg) {
return `<div class="invalid-feedback px-1">${msg}</div>`;
}
function iconLoading(loc) {
const element = `
<i id="${loc}-spinner" class="hg-menu-icon hg-status-icon remixicon-loader-4-line text-secondary"></i>`;
return element;
}
function iconError() {
const element = '<i class="hg-menu-icon hg-status-icon remixicon-alert-line"></i>';
return element;
}
function iconTimeout() {
const element = '<i class="remixicon-time-line"></i>';
return element;
}
function iconSuccess() {
const element = '<i class="hg-menu-icon hg-status-icon remixicon-check-line"></i>';
return element;
}
function supportedBtn(queryType) {
const element = `
<button class="btn btn-secondary hg-info-btn" id="hg-info-btn-${queryType}" data-hg-type="${queryType}" type="button">
<div id="hg-info-icon-${queryType}">
<i class="remixicon-information-line"></i>
</div>
</button>`;
return element;
}
function vrfSelect(title) {
const element = `<select class="form-control form-control-lg hg-select" id="query_vrf" title="${title}" disabled></select>`;
return element;
}
function frontEndAlert(msg) {
const element = `<div class="alert alert-danger text-center" id="hg-frontend-alert" role="alert">${msg}</div>`;
return element;
}
function vrfOption(txt) {
const element = `<option value="${txt}">${txt}</option>`;
return element;
}
function tagGroup(label, value) {
const element = `
<div class="input-group mb-3">
<div class="input-group-prepend">
${label}
</div>
<div class="input-group-append">
${value}
</div>
</div>`;
return element;
}
function tagLabel(color, id, text) {
const element = `
<span class="input-group-text hg-tag bg-${color}" id="hg-tag-${id}">
${text}
</span>`;
return element;
}
function resultsTitle(target, type, vrf, vrfText) {
const element = `
<div class="card-group flex-fill">
<div class="card">
<div class="card-body p-1 text-center">
<h5 class="card-title mb-1">${target}</h6>
<p class="card-text text-muted">${type}</p>
</div>
</div>
<div class="card">
<div class="card-body p-1 text-center">
<h5 class="card-title mb-1">${vrf}</h6>
<p class="card-text text-muted">${vrfText}</p>
</div>
</div>
</div>`;
return element;
}
function outputBlock(loc) {
const element = `
<div class="card" id="${loc}-output">
<div class="card-header bg-overlay" id="${loc}-heading">
<div class="float-right hg-status-container" id="${loc}-status-container">
<button type="button" class="float-right btn btn-loading btn-lg hg-menu-btn hg-status-btn"
data-location="${loc}" id="${loc}-status-btn" disabled>
</button>
</div>
<button type="button" class="float-right btn btn-loading btn-lg hg-menu-btn hg-copy-btn"
data-clipboard-target="#${loc}-text" id="${loc}-copy-btn" disabled>
<i class="remixicon-checkbox-multiple-blank-line hg-menu-icon hg-copy hg-copy-icon"></i>
</button>
<h2 class="mb-0" id="${loc}-heading-container">
<button class="btn btn-link" type="button" data-toggle="collapse"
data-target="#${loc}-content" aria-expanded="true" aria-controls="${loc}-content"
id="${loc}-heading-text">
</button>
</h2>
</div>
<div class="collapse" id="${loc}-content" aria-labelledby="${loc}-heading" data-parent="#hg-accordion">
<div class="card-body" id="${loc}-text">
</div>
</div>
</div>`;
return element;
}
module.exports = {
footerPopoverTemplate,
feedbackInvalid,
iconLoading,
iconError,
iconTimeout,
iconSuccess,
supportedBtn,
vrfSelect,
frontEndAlert,
vrfOption,
tagGroup,
tagLabel,
resultsTitle,
outputBlock,
};

View file

@ -1,27 +0,0 @@
import { frontEndAlert } from './components.es6';
import jQuery from '../node_modules/jquery';
class InputInvalid extends Error {
constructor(validationMsg, invalidField, fieldContainer) {
super(validationMsg, invalidField, fieldContainer);
this.name = this.constructor.name;
this.message = validationMsg;
this.field = invalidField;
this.container = fieldContainer;
}
}
class FrontEndError extends Error {
constructor(errorMsg, msgContainer) {
super(errorMsg, msgContainer);
this.name = this.constructor.name;
this.message = errorMsg;
this.container = msgContainer;
this.alert = frontEndAlert(this.message);
}
}
module.exports = {
InputInvalid,
FrontEndError,
};

View file

@ -1,340 +0,0 @@
// Module Imports
import format from '../node_modules/string-format';
import jQuery from '../node_modules/jquery';
import ClipboardJS from '../node_modules/clipboard';
// Project Imports
import {
footerPopoverTemplate,
feedbackInvalid,
supportedBtn,
vrfSelect,
vrfOption,
} from './components.es6';
import { InputInvalid, FrontEndError } from './errors.es6';
import {
swapSpacing, resetResults, reloadPage, findIntersection,
} from './util.es6';
import { queryApp } from './query.es6';
// JSON Config Import
import hgConf from './frontend.json';
// string-format config
format.extend(String.prototype, {});
const $ = jQuery;
const lgForm = $('#lgForm');
const vrfContainer = $('#hg-container-vrf');
const queryLocation = $('#location');
const queryType = $('#query_type');
const queryTargetAppend = $('#hg-target-append');
const submitIcon = $('#hg-submit-icon');
/* Removed liveSearch until bootstrap-select merges the fix for the mobile keyboard opening issue.
Basically, any time an option is selected on a mobile device, the keyboard pops open which is
super annoying. */
queryLocation.selectpicker({
iconBase: '',
liveSearch: false,
selectedTextFormat: 'count > 2',
style: '',
styleBase: 'form-control',
tickIcon: 'remixicon-check-line',
}).on('hidden.bs.select', (e) => {
$(e.currentTarget).nextAll('.dropdown-menu.show').find('input').blur();
});
queryType.selectpicker({
iconBase: '',
liveSearch: false,
style: '',
styleBase: 'form-control',
}).on('hidden.bs.select', (e) => {
$(e.currentTarget).nextAll('.form-control.dropdown-toggle').blur();
});
$('#hg-footer-terms-btn').popover({
html: true,
trigger: 'manual',
template: footerPopoverTemplate(),
placement: 'top',
content: $('#hg-footer-terms-html').html(),
}).on('click', (e) => {
$(e.currentTarget).popover('toggle');
}).on('focusout', (e) => {
$(e.currentTarget).popover('hide');
});
$('#hg-footer-help-btn').popover({
html: true,
trigger: 'manual',
placement: 'top',
template: footerPopoverTemplate(),
content: $('#hg-footer-help-html').html(),
}).on('click', (e) => {
$(e.currentTarget).popover('toggle');
}).on('focusout', (e) => {
$(e.currentTarget).popover('hide');
});
$('#hg-footer-credit-btn').popover({
html: true,
trigger: 'manual',
placement: 'top',
title: $('#hg-footer-credit-title').html(),
content: $('#hg-footer-credit-content').html(),
template: footerPopoverTemplate(),
}).on('click', (e) => {
$(e.currentTarget).popover('toggle');
}).on('focusout', (e) => {
$(e.currentTarget).popover('hide');
});
$(document).ready(() => {
reloadPage();
$('#hg-results').hide();
$('#hg-ratelimit-query').modal('hide');
if (location.pathname === '/') {
$('.animsition').animsition({
inClass: 'fade-in',
outClass: 'fade-out',
inDuration: 400,
outDuration: 400,
transition: (url) => { window.location.href = url; },
});
$('#hg-form').animsition('in');
}
});
queryType.on('changed.bs.select', (e) => {
const field = $(e.currentTarget);
const queryTypeId = field.val();
const queryTypeBtn = $('.hg-info-btn');
if (field.next('button').hasClass('is-invalid')) {
field.next('button').removeClass('is-invalid');
field.nextAll('.invalid-feedback').remove();
}
if ((queryTypeId === 'bgp_community') || (queryTypeId === 'bgp_aspath')) {
queryTypeBtn.remove();
queryTargetAppend.prepend(supportedBtn(queryTypeId));
} else {
queryTypeBtn.remove();
}
});
queryLocation.on('changed.bs.select', (e, clickedIndex, isSelected, previousValue) => {
const field = $(e.currentTarget);
if (field.next('button').hasClass('is-invalid')) {
field.next('button').removeClass('is-invalid');
field.nextAll('.invalid-feedback').remove();
}
vrfContainer.empty().removeClass('col');
const queryLocationIds = field.val();
if (Array.isArray(queryLocationIds) && (queryLocationIds.length)) {
const queryLocationNet = field[0][clickedIndex].dataset.netname;
const selectedVrfs = () => {
const allVrfs = [];
$.each(queryLocationIds, (i, loc) => {
const locVrfs = hgConf.networks[queryLocationNet][loc].vrfs;
allVrfs.push(new Set(locVrfs));
});
return allVrfs;
};
const intersectingVrfs = Array.from(findIntersection(...selectedVrfs()));
// Add the VRF select element
if (vrfContainer.find('#query_vrf').length === 0) {
vrfContainer.addClass('col').html(vrfSelect(hgConf.config.branding.text.query_vrf));
}
// Build the select options for each VRF in array
const vrfHtmlList = [];
$.each(intersectingVrfs, (i, vrf) => {
vrfHtmlList.push(vrfOption(vrf));
});
// Add the options to the VRF select element, enable it, initialize Bootstrap Select
vrfContainer.find('#query_vrf').html(vrfHtmlList.join('')).removeAttr('disabled').selectpicker({
iconBase: '',
liveSearch: false,
style: '',
styleBase: 'form-control',
});
if (intersectingVrfs.length === 0) {
vrfContainer.find('#query_vrf').selectpicker('destroy');
vrfContainer.find('#query_vrf').prop('title', hgConf.config.messages.no_matching_vrfs).prop('disabled', true);
vrfContainer.find('#query_vrf').selectpicker({
iconBase: '',
liveSearch: false,
style: '',
styleBase: 'form-control',
});
}
}
});
queryTargetAppend.on('click', '.hg-info-btn', () => {
const queryTypeId = $('.hg-info-btn').data('hg-type');
$(`#hg-info-${queryTypeId}`).modal('show');
});
$('#hg-row-2').find('#query_vrf').on('hidden.bs.select', (e) => {
$(e.currentTarget).nextAll('.form-control.dropdown-toggle').blur();
});
$(document).on('InvalidInputEvent', (e, domField) => {
const errorField = $(domField);
if (errorField.hasClass('is-invalid')) {
errorField.on('keyup', () => {
errorField.removeClass('is-invalid');
errorField.nextAll('.invalid-feedback').remove();
});
}
});
// Submit Form Action
lgForm.on('submit', (e) => {
e.preventDefault();
submitIcon.empty().html('<i class="remixicon-loader-4-line"></i>').addClass('hg-loading');
const queryType = $('#query_type').val() || '';
const queryTarget = $('#query_target').val() || '';
const queryVrf = $('#query_vrf').val() || hgConf.networks.default_vrf.display_name;
let queryLocation = $('#location').val() || [];
if (!Array.isArray(queryLocation)) {
queryLocation = new Array(queryLocation);
}
try {
/*
InvalidInput event positional arguments:
1: error message to display
2: thing to circle in red
3: place to put error message
*/
if (!queryTarget) {
const msgVars = { field: hgConf.config.branding.text.query_target };
throw new InputInvalid(
hgConf.config.messages.no_input.format(msgVars),
$('#query_target'),
$('#query_target').parent(),
);
}
if (!queryType) {
const msgVars = { field: hgConf.config.branding.text.query_type };
throw new InputInvalid(
hgConf.config.messages.no_input.format(msgVars),
$('#query_type').next('.dropdown-toggle'),
$('#query_type').next('.dropdown-toggle').parent(),
);
}
if (queryLocation === undefined || queryLocation.length === 0) {
const msgVars = { field: hgConf.config.branding.text.query_location };
throw new InputInvalid(
hgConf.config.messages.no_input.format(msgVars),
$('#location').next('.dropdown-toggle'),
$('#location').next('.dropdown-toggle').parent(),
);
}
} catch (err) {
$(err.field).addClass('is-invalid');
$(err.container).find('.invalid-feedback').remove();
$(err.container).append(feedbackInvalid(err.message));
submitIcon.empty().removeClass('hg-loading').html('<i class="remixicon-search-line"></i>');
$(document).trigger('InvalidInputEvent', err.field);
return false;
}
const queryTypeTitle = $(`#${queryType}`).data('display-name');
try {
try {
queryApp(
queryType,
queryTypeTitle,
queryLocation,
queryTarget,
queryVrf,
);
} catch (err) {
console.log(err);
throw new FrontEndError(
hgConf.config.messages.general,
lgForm,
);
}
} catch (err) {
err.container.append(err.alert);
submitIcon.empty().removeClass('hg-loading').html('<i class="remixicon-search-line"></i>');
return false;
}
$('#hg-form').animsition('out', $('#hg-results'), '#');
$('#hg-form').hide();
swapSpacing('results');
$('#hg-results').show();
$('#hg-results').animsition('in');
$('#hg-submit-spinner').remove();
$('#hg-back-btn').removeClass('d-none');
$('#hg-back-btn').animsition('in');
});
$('#hg-title-col').on('click', (e) => {
window.location = $(e.currentTarget).data('href');
return false;
});
$('#hg-back-btn').click(() => {
resetResults();
});
const clipboard = new ClipboardJS('.hg-copy-btn');
clipboard.on('success', (e) => {
const copyIcon = $(e.trigger).find('.hg-copy-icon');
copyIcon.removeClass('remixicon-checkbox-multiple-blank-line').addClass('remixicon-checkbox-multiple-line');
e.clearSelection();
setTimeout(() => {
copyIcon.removeClass('remixicon-checkbox-multiple-line').addClass('remixicon-checkbox-multiple-blank-line');
}, 800);
});
clipboard.on('error', (e) => {
console.log(e);
});
/*
.hg-done is the class added to the ${loc}-status-btn button component, once the
content has finished loading.
*/
// On hover, change icon to show that the content can be refreshed
$('#hg-accordion').on('mouseenter', '.hg-done', (e) => {
$(e.currentTarget)
.find('.hg-status-icon')
.addClass('remixicon-repeat-line');
});
$('#hg-accordion').on('mouseleave', '.hg-done', (e) => {
$(e.currentTarget)
.find('.hg-status-icon')
.removeClass('remixicon-repeat-line');
});
// On click, refresh the content
$('#hg-accordion').on('click', '.hg-done', (e) => {
const refreshQueryType = $('#query_type').val() || '';
const refreshQueryLocation = $('#location').val() || '';
const refreshQueryTarget = $('#query_target').val() || '';
const refreshQueryVrf = $('#query_vrf').val() || '';
const refreshQueryTypeTitle = $(`#${refreshQueryType}`).data('display-name');
queryApp(
refreshQueryType,
refreshQueryTypeTitle,
refreshQueryLocation,
refreshQueryTarget,
refreshQueryVrf,
);
});
$('#hg-ratelimit-query').on('shown.bs.modal', () => {
$('#hg-ratelimit-query').trigger('focus');
});
$('#hg-ratelimit-query').find('btn').on('click', () => {
$('#hg-ratelimit-query').modal('hide');
});

View file

@ -1,167 +0,0 @@
import {
iconLoading,
iconError,
iconTimeout,
iconSuccess,
tagGroup,
tagLabel,
resultsTitle,
outputBlock,
} from './components.es6';
import jQuery from '../node_modules/jquery';
import hgConf from './frontend.json';
import { resetResults } from './util.es6';
const $ = jQuery;
function queryApp(queryType, queryTypeName, locationList, queryTarget, queryVrf) {
// $('#hg-results-title').html(
// tagGroup(
// tagLabel(
// 'loading',
// 'query-type',
// queryTypeName,
// ),
// tagLabel(
// 'primary',
// 'query-target',
// queryTarget,
// ),
// )
// + tagGroup(
// tagLabel(
// 'loading',
// 'query-vrf-loc',
// locationList.join(', '),
// ),
// tagLabel(
// 'secondary',
// 'query-target',
// queryVrf,
// ),
// ),
// );
$('#hg-results-title').html(
resultsTitle(queryTarget, queryTypeName, queryVrf, hgConf.config.branding.text.query_vrf),
);
$('#hg-submit-icon')
.empty()
.removeClass('hg-loading')
.html('<i class="remixicon-search-line"></i>');
$.each(locationList, (n, loc) => {
const locationName = $(`#${loc}`).data('display-name');
const contentHtml = outputBlock(loc);
if ($(`#${loc}-output`).length) {
$(`#${loc}-output`).replaceWith(contentHtml);
} else {
$('#hg-accordion').append(contentHtml);
}
$(`#${loc}-heading-text`).text(locationName);
$(`#${loc}-status-container`)
.addClass('hg-loading')
.find('.hg-status-btn')
.empty()
.html(iconLoading(loc));
const generateError = (errorClass, locError, text) => {
$(`#${locError}-heading`)
.removeClass('bg-overlay')
.addClass(`bg-${errorClass}`);
$(`#${locError}-heading`)
.find('.hg-menu-btn')
.removeClass('btn-loading')
.addClass(`btn-${errorClass}`);
$(`#${locError}-status-container`)
.removeClass('hg-loading')
.find('.hg-status-btn')
.empty()
.html(iconError)
.addClass('hg-done');
$(`#${locError}-text`).html(text);
};
const timeoutError = (locError, text) => {
$(`#${locError}-heading`)
.removeClass('bg-overlay')
.addClass('bg-warning');
$(`#${locError}-heading`)
.find('.hg-menu-btn')
.removeClass('btn-loading')
.addClass('btn-warning');
$(`#${locError}-status-container`)
.removeClass('hg-loading')
.find('.hg-status-btn')
.empty()
.html(iconTimeout)
.addClass('hg-done');
$(`#${locError}-text`)
.empty()
.html(text);
};
$.ajax({
url: '/query',
method: 'POST',
data: JSON.stringify({
query_location: loc,
query_type: queryType,
query_target: queryTarget,
query_vrf: queryVrf,
response_format: 'html',
}),
contentType: 'application/json; charset=utf-8',
context: document.body,
async: true,
timeout: hgConf.config.general.request_timeout * 1000,
})
.done((data, textStatus, jqXHR) => {
const displayHtml = `<pre>${data.output}</pre>`;
$(`#${loc}-heading`)
.removeClass('bg-overlay')
.addClass('bg-primary');
$(`#${loc}-heading`)
.find('.hg-menu-btn')
.removeClass('btn-loading')
.addClass('btn-primary');
$(`#${loc}-status-container`)
.removeClass('hg-loading')
.find('.hg-status-btn')
.empty()
.html(iconSuccess)
.addClass('hg-done');
$(`#${loc}-text`)
.empty()
.html(displayHtml);
})
.fail((jqXHR, textStatus, errorThrown) => {
const statusCode = jqXHR.status;
if (textStatus === 'timeout') {
timeoutError(loc, hgConf.config.messages.request_timeout);
} else if (jqXHR.status === 429) {
resetResults();
$('#hg-ratelimit-query').modal('show');
} else if (statusCode === 500 && textStatus !== 'timeout') {
timeoutError(loc, hgConf.config.messages.request_timeout);
} else if (
jqXHR.responseJSON.alert === 'danger'
|| jqXHR.responseJSON.alert === 'warning'
) {
generateError(jqXHR.responseJSON.alert, loc, jqXHR.responseJSON.output);
}
})
.always(() => {
$(`#${loc}-status-btn`).removeAttr('disabled');
$(`#${loc}-copy-btn`).removeAttr('disabled');
});
$(`#${locationList[0]}-content`).collapse('show');
});
}
module.exports = {
queryApp,
};

View file

@ -1,76 +0,0 @@
// Module Imports
import jQuery from '../node_modules/jquery';
const $ = jQuery;
const pageContainer = $('#hg-page-container');
const formContainer = $('#hg-form');
const titleColumn = $('#hg-title-col');
const queryLocation = $('#location');
const queryType = $('#query_type');
const queryTarget = $('#query_target');
const queryVrf = $('#query_vrf');
const resultsContainer = $('#hg-results');
const resultsAccordion = $('#hg-accordion');
const resultsColumn = resultsAccordion.parent();
const backButton = $('#hg-back-btn');
function swapSpacing(goTo) {
if (goTo === 'form') {
pageContainer.removeClass('px-0 px-md-3');
resultsColumn.removeClass('px-0');
titleColumn.removeClass('text-center');
} else if (goTo === 'results') {
pageContainer.addClass('px-0 px-md-3');
resultsColumn.addClass('px-0');
titleColumn.addClass('text-left');
}
}
function resetResults() {
queryLocation.selectpicker('deselectAll');
queryLocation.selectpicker('val', '');
queryType.selectpicker('val', '');
queryTarget.val('');
queryVrf.val('');
resultsContainer.animsition('out', formContainer, '#');
resultsContainer.hide();
$('.hg-info-btn').remove();
swapSpacing('form');
formContainer.show();
formContainer.animsition('in');
backButton.addClass('d-none');
resultsAccordion.empty();
}
function reloadPage() {
queryLocation.selectpicker('deselectAll');
queryLocation.selectpicker('val', []);
queryType.selectpicker('val', '');
queryTarget.val('');
queryVrf.val('');
resultsAccordion.empty();
}
function findIntersection(firstSet, ...sets) {
const count = sets.length;
const result = new Set(firstSet);
firstSet.forEach((item) => {
let i = count;
let allHave = true;
while (i--) {
allHave = sets[i].has(item);
if (!allHave) { break; }
}
if (!allHave) {
result.delete(item);
}
});
return result;
}
module.exports = {
swapSpacing,
resetResults,
reloadPage,
findIntersection,
};

View file

@ -1,42 +0,0 @@
{
"name": "hyperglass",
"version": "1.0.0",
"author": "https://github.com/checktheroads",
"license": "BSD-3-Clause-Clear",
"description": "Front end utilities for hyperglass",
"main": "hyperglass.js",
"dependencies": {
"@babel/core": "^7.0.0-0",
"@babel/preset-env": "^7.5.5",
"animsition": "^4.0.2",
"bootstrap": "^4.3.1",
"bootstrap-select": "^1.13.10",
"clipboard": "^2.0.4",
"get-google-fonts": "^1.2.0",
"global": "^4.4.0",
"jquery": "^3.4.1",
"parcel-bundler": "^1.12.3",
"popper.js": "^1.15.0",
"remixicon": "^1.3.1",
"sass": "^1.22.10",
"string-format": "^2.0.0",
"tinyify": "^2.5.1",
"watchify": "^3.11.1"
},
"devDependencies": {
"@babel/preset-react": "^7.0.0",
"babel-core": "^6.26.3",
"cssnano": "^4.1.10",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.2.0",
"eslint-plugin-import": "^2.18.2"
},
"scripts": {
"build": "parcel build --no-cache --bundle-node-modules --public-url /ui/ --out-dir ../ui --out-file hyperglass.js bundle.es6 && parcel build --no-cache --bundle-node-modules --public-url /ui/ --out-dir ../ui --out-file hyperglass.css bundle.css"
},
"babel": {
"presets": [
"@babel/preset-react"
]
}
}

View file

@ -1,13 +0,0 @@
.DS_Store
# dev/test files
*.tmp*
test*
*.log
# generated theme file from hyperglass/hyperglass/render/templates/theme.sass.j2
theme.sass
# generated JSON file from ingested & validated YAML config
frontend.json
# NPM modules
node_modules/
# Downloaded Google Fonts
fonts/

View file

@ -1,7 +0,0 @@
// Custom Classes
// Imports
@import "./fonts/fonts.css";
@import "./theme.sass";
@import "./node_modules/bootstrap/scss/bootstrap";
@import "./overrides.sass";

View file

@ -1,467 +0,0 @@
// Custom Utility Classes
@media (min-width: 576px)
.mw-sm-25
max-width: 25% !important
.mw-sm-50
max-width: 50% !important
.mw-sm-75
max-width: 75% !important
.mw-sm-100
max-width: 100% !important
.mw-sm-none
max-width: none !important
.mh-sm-25
max-height: 25% !important
.mh-sm-50
max-height: 50% !important
.mh-sm-75
max-height: 75% !important
.mh-sm-100
max-height: 100% !important
.mh-sm-none
max-height: none !important
@media (min-width: 768px)
.mw-md-25
max-width: 25% !important
.mw-md-50
max-width: 50% !important
.mw-md-75
max-width: 75% !important
.mw-md-100
max-width: 100% !important
.mw-md-none
max-width: none !important
.mh-md-25
max-height: 25% !important
.mh-md-50
max-height: 50% !important
.mh-md-75
max-height: 75% !important
.mh-md-100
max-height: 100% !important
.mh-md-none
max-height: none !important
@media (min-width: 992px)
.mw-lg-25
max-width: 25% !important
.mw-lg-50
max-width: 50% !important
.mw-lg-75
max-width: 75% !important
.mw-lg-100
max-width: 100% !important
.mw-lg-none
max-width: none !important
.mh-lg-25
max-height: 25% !important
.mh-lg-50
max-height: 50% !important
.mh-lg-75
max-height: 75% !important
.mh-lg-100
max-height: 100% !important
.mh-lg-none
max-height: none !important
@media (min-width: 1200px)
.mw-xl-25
max-width: 25% !important
.mw-xl-50
max-width: 50% !important
.mw-xl-75
max-width: 75% !important
.mw-xl-100
max-width: 100% !important
.mw-xl-none
max-width: none !important
.mh-xl-25
max-height: 25% !important
.mh-xl-50
max-height: 50% !important
.mh-xl-75
max-height: 75% !important
.mh-xl-100
max-height: 100% !important
.mh-xl-none
max-height: none !important
@media (min-width: 576px)
.mw-sm-25
max-width: 25% !important
.mw-sm-50
max-width: 50% !important
.mw-sm-75
max-width: 75% !important
.mw-sm-100
max-width: 100% !important
.mw-sm-none
max-width: none !important
.mh-sm-25
max-height: 25% !important
.mh-sm-50
max-height: 50% !important
.mh-sm-75
max-height: 75% !important
.mh-sm-100
max-height: 100% !important
.mh-sm-none
max-height: none !important
@media (min-width: 768px)
.mw-md-25
max-width: 25% !important
.mw-md-50
max-width: 50% !important
.mw-md-75
max-width: 75% !important
.mw-md-100
max-width: 100% !important
.mw-md-none
max-width: none !important
.mh-md-25
max-height: 25% !important
.mh-md-50
max-height: 50% !important
.mh-md-75
max-height: 75% !important
.mh-md-100
max-height: 100% !important
.mh-md-none
max-height: none !important
@media (min-width: 992px)
.mw-lg-25
max-width: 25% !important
.mw-lg-50
max-width: 50% !important
.mw-lg-75
max-width: 75% !important
.mw-lg-100
max-width: 100% !important
.mw-lg-none
max-width: none !important
.mh-lg-25
max-height: 25% !important
.mh-lg-50
max-height: 50% !important
.mh-lg-75
max-height: 75% !important
.mh-lg-100
max-height: 100% !important
.mh-lg-none
max-height: none !important
@media (min-width: 1200px)
.mw-xl-25
max-width: 25% !important
.mw-xl-50
max-width: 50% !important
.mw-xl-75
max-width: 75% !important
.mw-xl-100
max-width: 100% !important
.mw-xl-none
max-width: none !important
.mh-xl-25
max-height: 25% !important
.mh-xl-50
max-height: 50% !important
.mh-xl-75
max-height: 75% !important
.mh-xl-100
max-height: 100% !important
.mh-xl-none
max-height: none !important
.mh-75
max-height: 75% !important
// hyperglass overrides
.hg-logo
max-height: 60px
max-width: 100%
height: auto
width: auto
#hg-submit-button
border-bottom-right-radius: $border-radius-lg !important
border-top-right-radius: $border-radius-lg !important
#hg-form
margin-top: 15% !important
margin-bottom: 10% !important
padding: unset
#hg-results
margin-top: 5% !important
margin-bottom: 5% !important
padding: unset
#hg-results-title
line-height: unset
.nav-masthead
.nav-link
& + .nav-link
margin-left: 1rem
.input-group-lg > .form-control:not(textarea),
.input-group-lg > .custom-select,
.bootstrap-select.form-control-lg .dropdown-toggle, .input-group-append > .btn
height: $input-height-lg !important
padding: $input-padding-y-lg $input-padding-x-lg !important
.form-control
transition: background-color .15s ease-in-out,box-shadow .15s ease-in-out !important
color: $input-color !important
.bootstrap-select > .dropdown-toggle.bs-placeholder
color: $input-placeholder-color !important
.bootstrap-select .dropdown-menu li a.dropdown-item:not(.selected):hover
background-color: $hg-overlay
color: findTextColor($hg-overlay)
.hg-back:hover
text-decoration: none
border: 1px solid $hg-primary !important
.hg-back:focus
text-decoration: none
.hg-back
position: fixed !important
width: 3rem !important
height: 3rem !important
left: 2rem !important
bottom: 5rem !important
border-radius: 50rem !important
border: 1px solid $card-border-color !important
background-color: $hg-field-bg !important
transition: all .3s !important
font-weight: 200 !important
// font-size: 1.4rem !important
font-size: $font-size-lg
opacity: 1 !important
pointer-events: auto !important
#hg-accordion
.btn-link
font-weight: 200
// font-size: 1.4rem
font-size: $font-size-lg
#hg-accordion
.card-body
word-wrap: normal !important
color: findTextColor($card-bg) !important
.hg-menu-btn
-webkit-transition: none
-moz-transition: none
-o-transition: none
transition: none
.hg-menu-icon
-webkit-transition: all 0.5s ease
-moz-transition: all 0.5s ease
-o-transition: all 0.5s ease
transition: all 0.5s ease
#hg-title-col
cursor: pointer
.tab-content
overflow: hidden
.tab-pane
& > pre
min-height: 45vh
max-height: 60vh
-ms-overflow-style: none
scrollbar-width: none
::-webkit-scrollbar
width: 0px
background: transparent
.hg-status-container
display: inline-block
text-align: center
vertical-align: middle
line-height: 1.5
.hg-loading
animation: spinner-border .75s linear infinite
.accordion > .card:only-of-type
border-bottom: $card-border-width solid $card-border-color
border-top-left-radius: $border-radius !important
border-top-right-radius: $border-radius !important
border-bottom-left-radius: $border-radius !important
border-bottom-right-radius: $border-radius !important
.btn:focus,.btn:active
outline: none !important
box-shadow: none
.bg-primary
.btn-primary:hover
color: findTextColor($hg-primary) !important
background-color: darken($hg-primary, 10%) !important
border-color: $hg-primary !important
.bg-warning
.btn-warning:hover
color: findTextColor($hg-warning) !important
background-color: darken($hg-warning, 10%) !important
border-color: $hg-warning !important
.bg-danger
.btn-danger:hover
color: findTextColor($hg-danger) !important
background-color: darken($hg-danger, 10%) !important
border-color: $hg-danger !important
.bg-overlay
.btn-overlay:hover
color: findTextColor($hg-overlay) !important
background-color: darken($hg-overlay, 10%) !important
border-color: $hg-overlay !important
.bg-primary
*
color: findTextColor($hg-primary) !important
.bg-danger
*
color: findTextColor($hg-danger) !important
.bg-warning
*
color: findTextColor($hg-warning) !important
.bg-overlay
*
color: findTextColor($hg-overlay) !important
.bg-primary
::selection, ::-moz-selection
background-color: findTextColor($hg-primary) !important
color: $hg-primary !important
.bg-danger
::selection, ::-moz-selection
background-color: findTextColor($hg-danger) !important
color: $hg-danger !important
.bg-warning
::selection, ::-moz-selection
background-color: findTextColor($hg-warning) !important
color: $hg-warning !important
.bg-overlay
::selection, ::-moz-selection
background-color: findTextColor($hg-overlay) !important
color: $hg-overlay !important
.bg-danger
.btn-outline-danger
border-color: findTextColor($hg-danger) !important
.bg-danger
.btn-outline-danger:hover
background-color: findTextColor($hg-danger) !important
color: $hg-danger !important
.bg-danger
hr
background-color: darken($hg-danger, 10%)
.modal-body > p
padding-left: 0.3rem !important
.modal-title
padding-bottom: 1rem !important
.popover-body
max-height: 60vh !important
overflow-y: auto !important
.jumbotron.bg-danger
margin-top: 10% !important
margin-bottom: 10% !important

File diff suppressed because it is too large Load diff

View file

@ -1,8 +0,0 @@
*.css
*.js
*.map
*.woff
*.woff2
*.svg
*.ttf
*.eot

5
ui/.alias.js Normal file
View file

@ -0,0 +1,5 @@
const path = require("path");
module.exports = {
"~": path.join(__dirname)
};

1
ui/.next/BUILD_ID Normal file
View file

@ -0,0 +1 @@
8MqatFOx_lsVMBtOoS25-

View file

@ -0,0 +1,28 @@
{
"devFiles": [],
"pages": {
"/": [
"static/runtime/webpack-bc0bd5fbb696f901c79f.js",
"static/chunks/commons.aec5eef0c3bc0db3cfad.js",
"static/runtime/main-93cb3e6f7aab28bb61f5.js"
],
"/_app": [
"static/runtime/webpack-bc0bd5fbb696f901c79f.js",
"static/chunks/commons.aec5eef0c3bc0db3cfad.js",
"static/runtime/main-93cb3e6f7aab28bb61f5.js"
],
"/_error": [
"static/runtime/webpack-bc0bd5fbb696f901c79f.js",
"static/chunks/commons.aec5eef0c3bc0db3cfad.js",
"static/runtime/main-93cb3e6f7aab28bb61f5.js"
],
"/_polyfills": [
"static/runtime/polyfills-104006257fab7491bcba.js"
],
"/index": [
"static/runtime/webpack-bc0bd5fbb696f901c79f.js",
"static/chunks/commons.aec5eef0c3bc0db3cfad.js",
"static/runtime/main-93cb3e6f7aab28bb61f5.js"
]
}
}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _interopRequireDefault = require(\"@babel/runtime-corejs2/helpers/interopRequireDefault\");\n\nexports.__esModule = true;\nexports.Container = Container;\nexports.createUrl = createUrl;\nexports.default = void 0;\n\nvar _extends2 = _interopRequireDefault(require(\"@babel/runtime-corejs2/helpers/extends\"));\n\nvar _asyncToGenerator2 = _interopRequireDefault(require(\"@babel/runtime-corejs2/helpers/asyncToGenerator\"));\n\nvar _react = _interopRequireDefault(require(\"react\"));\n\nvar _utils = require(\"../next-server/lib/utils\");\n\nexports.AppInitialProps = _utils.AppInitialProps;\n\nrequire(\"../client/router\");\n/**\n* `App` component is used for initialize of pages. It allows for overwriting and full control of the `page` initialization.\n* This allows for keeping state between navigation, custom error handling, injecting additional data.\n*/\n\n\nfunction appGetInitialProps(_x) {\n return _appGetInitialProps.apply(this, arguments);\n}\n\nfunction _appGetInitialProps() {\n _appGetInitialProps = (0, _asyncToGenerator2.default)(function* (_ref) {\n var {\n Component,\n ctx\n } = _ref;\n var pageProps = yield (0, _utils.loadGetInitialProps)(Component, ctx);\n return {\n pageProps\n };\n });\n return _appGetInitialProps.apply(this, arguments);\n}\n\nclass App extends _react.default.Component {\n // Kept here for backwards compatibility.\n // When someone ended App they could call `super.componentDidCatch`.\n // @deprecated This method is no longer needed. Errors are caught at the top level\n componentDidCatch(error, _errorInfo) {\n throw error;\n }\n\n render() {\n var {\n router,\n Component,\n pageProps\n } = this.props;\n var url = createUrl(router);\n return _react.default.createElement(Component, (0, _extends2.default)({}, pageProps, {\n url: url\n }));\n }\n\n}\n\nexports.default = App;\nApp.origGetInitialProps = appGetInitialProps;\nApp.getInitialProps = appGetInitialProps;\nvar warnContainer;\nvar warnUrl;\n\nif (false) {\n warnContainer = (0, _utils.execOnce)(() => {\n console.warn(\"Warning: the `Container` in `_app` has been deprecated and should be removed. https://err.sh/zeit/next.js/app-container-deprecated\");\n });\n warnUrl = (0, _utils.execOnce)(() => {\n console.error(\"Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated\");\n });\n} // @deprecated noop for now until removal\n\n\nfunction Container(p) {\n if (false) warnContainer();\n return p.children;\n}\n\nfunction createUrl(router) {\n // This is to make sure we don't references the router object at call time\n var {\n pathname,\n asPath,\n query\n } = router;\n return {\n get query() {\n if (false) warnUrl();\n return query;\n },\n\n get pathname() {\n if (false) warnUrl();\n return pathname;\n },\n\n get asPath() {\n if (false) warnUrl();\n return asPath;\n },\n\n back: () => {\n if (false) warnUrl();\n router.back();\n },\n push: (url, as) => {\n if (false) warnUrl();\n return router.push(url, as);\n },\n pushTo: (href, as) => {\n if (false) warnUrl();\n var pushRoute = as ? href : '';\n var pushUrl = as || href;\n return router.push(pushRoute, pushUrl);\n },\n replace: (url, as) => {\n if (false) warnUrl();\n return router.replace(url, as);\n },\n replaceTo: (href, as) => {\n if (false) warnUrl();\n var replaceRoute = as ? href : '';\n var replaceUrl = as || href;\n return router.replace(replaceRoute, replaceUrl);\n }\n };\n}","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Set = require(\"@babel/runtime-corejs2/core-js/set\");\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst react_1 = require(\"react\");\n\nconst isServer = true;\n\nexports.default = () => {\n const mountedInstances = new _Set();\n let state;\n\n function emitChange(component) {\n state = component.props.reduceComponentsToState([...mountedInstances], component.props);\n\n if (component.props.handleStateChange) {\n component.props.handleStateChange(state);\n }\n }\n\n return class extends react_1.Component {\n // Used when server rendering\n static rewind() {\n const recordedState = state;\n state = undefined;\n mountedInstances.clear();\n return recordedState;\n }\n\n constructor(props) {\n super(props);\n\n if (isServer) {\n mountedInstances.add(this);\n emitChange(this);\n }\n }\n\n componentDidMount() {\n mountedInstances.add(this);\n emitChange(this);\n }\n\n componentDidUpdate() {\n emitChange(this);\n }\n\n componentWillUnmount() {\n mountedInstances.delete(this);\n emitChange(this);\n }\n\n render() {\n return null;\n }\n\n };\n};","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\nvar __importStar = this && this.__importStar || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\n result[\"default\"] = mod;\n return result;\n};\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst React = __importStar(require(\"react\"));\n\nexports.RouterContext = React.createContext(null);","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Input, useColorMode, useTheme } from \"@chakra-ui/core\";\nconst StyledInput = styled(Input)`\n &::placeholder {\n color: ${props => props.placeholderColor};\n }\n`;\nexport default (({\n placeholder,\n register\n}) => {\n const theme = useTheme();\n const {\n colorMode\n } = useColorMode();\n const bg = colorMode === \"dark\" ? theme.colors.whiteAlpha[100] : theme.colors.white;\n const color = colorMode === \"dark\" ? theme.colors.whiteAlpha[800] : theme.colors.gray[400];\n const border = colorMode === \"dark\" ? theme.colors.whiteAlpha[50] : theme.colors.gray[100];\n const borderRadius = theme.space[1];\n const placeholderColor = colorMode === \"dark\" ? theme.colors.whiteAlpha[400] : theme.colors.gray[400];\n return __jsx(StyledInput, {\n name: \"query_target\",\n ref: register,\n placeholder: placeholder,\n placeholderColor: placeholderColor,\n size: \"lg\",\n bg: bg,\n color: color,\n borderColor: border,\n borderRadius: borderRadius\n });\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport ChakraSelect from \"~/components/ChakraSelect\";\n\nconst buildQueries = queryTypes => {\n const queries = [];\n queryTypes.map(q => {\n queries.push({\n value: q.name,\n label: q.display_name\n });\n });\n return queries;\n};\n\nexport default (({\n queryTypes,\n onChange\n}) => {\n const queries = buildQueries(queryTypes);\n return __jsx(ChakraSelect, {\n size: \"lg\",\n name: \"query_type\",\n onChange: e => onChange({\n field: \"query_type\",\n value: e.value\n }),\n options: queries\n });\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"import chroma from \"chroma-js\";\n\nvar isDark = function isDark(color) {\n // YIQ equation from http://24ways.org/2010/calculating-color-contrast\n var rgb = chroma(color).rgb();\n var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n return yiq < 128;\n};\n\nvar isLight = function isLight(color) {\n return isDark(color);\n};\n\nvar opposingColor = function opposingColor(theme, color) {\n var opposing = isDark(color) ? theme.colors.white : theme.colors.black;\n return opposing;\n};\n\nvar googleFontUrl = function googleFontUrl(fontFamily) {\n var weights = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [300, 400, 700];\n var urlWeights = weights.join(\",\");\n var fontName = fontFamily.split(/, /)[0].trim().replace(/'|\"/g, \"\");\n var urlFont = fontName.split(/ /).join(\"+\");\n var urlBase = \"https://fonts.googleapis.com/css?family=\".concat(urlFont, \":\").concat(urlWeights, \"&display=swap\");\n return urlBase;\n};\n\nexport { isDark, isLight, opposingColor, googleFontUrl };","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction rewriteUrlForNextExport(url) {\n const [pathname, hash] = url.split('#'); // tslint:disable-next-line\n\n let [path, qs] = pathname.split('?');\n path = path.replace(/\\/$/, ''); // Append a trailing slash if this path does not have an extension\n\n if (!/\\.[^/]+\\/?$/.test(path)) path += `/`;\n if (qs) path += '?' + qs;\n if (hash) path += '#' + hash;\n return path;\n}\n\nexports.rewriteUrlForNextExport = rewriteUrlForNextExport;","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Object$keys = require(\"@babel/runtime-corejs2/core-js/object/keys\");\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction getRouteMatcher(routeRegex) {\n const {\n re,\n groups\n } = routeRegex;\n return pathname => {\n const routeMatch = re.exec(pathname);\n\n if (!routeMatch) {\n return false;\n }\n\n const decode = decodeURIComponent;\n const params = {};\n\n _Object$keys(groups).forEach(slugName => {\n const g = groups[slugName];\n const m = routeMatch[g.pos];\n\n if (m !== undefined) {\n params[slugName] = ~m.indexOf('/') ? m.split('/').map(entry => decode(entry)) : g.repeat ? [decode(m)] : decode(m);\n }\n });\n\n return params;\n };\n}\n\nexports.getRouteMatcher = getRouteMatcher;","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport { Flex, IconButton, useColorMode, useTheme } from \"@chakra-ui/core\";\nimport { motion } from \"framer-motion\";\nvar AnimatedFlex = motion.custom(Flex);\nexport default (function () {\n var theme = useTheme();\n\n var _useColorMode = useColorMode(),\n colorMode = _useColorMode.colorMode,\n toggleColorMode = _useColorMode.toggleColorMode;\n\n var bg = {\n light: theme.colors.white,\n dark: theme.colors.black\n };\n var icon = {\n light: \"moon\",\n dark: \"sun\"\n };\n return __jsx(Flex, {\n position: \"fixed\",\n as: \"header\",\n top: \"0\",\n zIndex: \"4\",\n bg: bg[colorMode],\n color: theme.colors.gray[500],\n left: \"0\",\n right: \"0\",\n width: \"full\",\n height: \"4rem\"\n }, __jsx(Flex, {\n w: \"100%\",\n mx: \"auto\",\n px: 6,\n justifyContent: \"flex-end\"\n }, __jsx(AnimatedFlex, {\n align: \"center\",\n initial: {\n opacity: 0\n },\n animate: {\n opacity: 1\n },\n transition: {\n duration: 0.6\n }\n }, __jsx(IconButton, {\n \"aria-label\": \"Switch to \".concat(colorMode === \"light\" ? \"dark\" : \"light\", \" mode\"),\n variant: \"ghost\",\n color: \"current\",\n ml: \"2\",\n fontSize: \"20px\",\n onClick: toggleColorMode,\n icon: icon[colorMode]\n }))));\n});","map":null,"metadata":{},"sourceType":"module"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Set = require(\"@babel/runtime-corejs2/core-js/set\");\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst react_1 = __importDefault(require(\"react\"));\n\nconst side_effect_1 = __importDefault(require(\"./side-effect\"));\n\nconst amp_context_1 = require(\"./amp-context\");\n\nconst head_manager_context_1 = require(\"./head-manager-context\");\n\nconst amp_1 = require(\"./amp\");\n\nfunction defaultHead(inAmpMode = false) {\n const head = [react_1.default.createElement(\"meta\", {\n charSet: \"utf-8\"\n })];\n\n if (!inAmpMode) {\n head.push(react_1.default.createElement(\"meta\", {\n name: \"viewport\",\n content: \"width=device-width,minimum-scale=1,initial-scale=1\"\n }));\n }\n\n return head;\n}\n\nexports.defaultHead = defaultHead;\n\nfunction onlyReactElement(list, child) {\n // React children can be \"string\" or \"number\" in this case we ignore them for backwards compat\n if (typeof child === 'string' || typeof child === 'number') {\n return list;\n } // Adds support for React.Fragment\n\n\n if (child.type === react_1.default.Fragment) {\n return list.concat(react_1.default.Children.toArray(child.props.children).reduce((fragmentList, fragmentChild) => {\n if (typeof fragmentChild === 'string' || typeof fragmentChild === 'number') {\n return fragmentList;\n }\n\n return fragmentList.concat(fragmentChild);\n }, []));\n }\n\n return list.concat(child);\n}\n\nconst METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp'];\n/*\n returns a function for filtering head child elements\n which shouldn't be duplicated, like <title/>\n Also adds support for deduplicated `key` properties\n*/\n\nfunction unique() {\n const keys = new _Set();\n const tags = new _Set();\n const metaTypes = new _Set();\n const metaCategories = {};\n return h => {\n let unique = true;\n\n if (h.key && typeof h.key !== 'number' && h.key.indexOf('$') > 0) {\n const key = h.key.slice(h.key.indexOf('$') + 1);\n\n if (keys.has(key)) {\n unique = false;\n } else {\n keys.add(key);\n }\n } // eslint-disable-next-line default-case\n\n\n switch (h.type) {\n case 'title':\n case 'base':\n if (tags.has(h.type)) {\n unique = false;\n } else {\n tags.add(h.type);\n }\n\n break;\n\n case 'meta':\n for (let i = 0, len = METATYPES.length; i < len; i++) {\n const metatype = METATYPES[i];\n if (!h.props.hasOwnProperty(metatype)) continue;\n\n if (metatype === 'charSet') {\n if (metaTypes.has(metatype)) {\n unique = false;\n } else {\n metaTypes.add(metatype);\n }\n } else {\n const category = h.props[metatype];\n const categories = metaCategories[metatype] || new _Set();\n\n if (categories.has(category)) {\n unique = false;\n } else {\n categories.add(category);\n metaCategories[metatype] = categories;\n }\n }\n }\n\n break;\n }\n\n return unique;\n };\n}\n/**\n *\n * @param headElement List of multiple <Head> instances\n */\n\n\nfunction reduceComponents(headElements, props) {\n return headElements.reduce((list, headElement) => {\n const headElementChildren = react_1.default.Children.toArray(headElement.props.children);\n return list.concat(headElementChildren);\n }, []).reduce(onlyReactElement, []).reverse().concat(defaultHead(props.inAmpMode)).filter(unique()).reverse().map((c, i) => {\n const key = c.key || i;\n return react_1.default.cloneElement(c, {\n key\n });\n });\n}\n\nconst Effect = side_effect_1.default();\n/**\n * This component injects elements to `<head>` of your page.\n * To avoid duplicated `tags` in `<head>` you can use the `key` property, which will make sure every tag is only rendered once.\n */\n\nfunction Head({\n children\n}) {\n return react_1.default.createElement(amp_context_1.AmpStateContext.Consumer, null, ampState => react_1.default.createElement(head_manager_context_1.HeadManagerContext.Consumer, null, updateHead => react_1.default.createElement(Effect, {\n reduceComponentsToState: reduceComponents,\n handleStateChange: updateHead,\n inAmpMode: amp_1.isInAmpMode(ampState)\n }, children)));\n}\n\nHead.rewind = Effect.rewind;\nexports.default = Head;","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _slicedToArray = require(\"@babel/runtime-corejs2/helpers/slicedToArray\");\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction rewriteUrlForNextExport(url) {\n var _url$split = url.split('#'),\n _url$split2 = _slicedToArray(_url$split, 2),\n pathname = _url$split2[0],\n hash = _url$split2[1]; // tslint:disable-next-line\n\n\n var _pathname$split = pathname.split('?'),\n _pathname$split2 = _slicedToArray(_pathname$split, 2),\n path = _pathname$split2[0],\n qs = _pathname$split2[1];\n\n path = path.replace(/\\/$/, ''); // Append a trailing slash if this path does not have an extension\n\n if (!/\\.[^/]+\\/?$/.test(path)) path += \"/\";\n if (qs) path += '?' + qs;\n if (hash) path += '#' + hash;\n return path;\n}\n\nexports.rewriteUrlForNextExport = rewriteUrlForNextExport;","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport { Box, useColorMode, useTheme } from \"@chakra-ui/core\";\nexport default (({\n children\n}) => {\n const {\n colorMode\n } = useColorMode();\n const theme = useTheme();\n const bg = {\n dark: theme.colors.gray[800],\n light: theme.colors.blackAlpha[100]\n };\n const color = {\n dark: theme.colors.white,\n light: theme.colors.black\n };\n return __jsx(Box, {\n fontFamily: \"mono\",\n mt: 5,\n p: 3,\n border: \"1px\",\n borderColor: \"inherit\",\n rounded: \"md\",\n bg: bg[colorMode],\n color: color[colorMode],\n fontSize: \"sm\",\n whiteSpace: \"pre-wrap\",\n as: \"pre\"\n }, children);\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"import _extends from \"@babel/runtime-corejs2/helpers/esm/extends\";\nimport _objectWithoutProperties from \"@babel/runtime-corejs2/helpers/esm/objectWithoutProperties\";\nvar __jsx = React.createElement;\nimport React from \"react\";\nimport { Flex, FormControl, FormLabel, FormErrorMessage, useTheme, useColorMode } from \"@chakra-ui/core\";\nimport HelpModal from \"~/components/HelpModal\";\nexport default (function (_ref) {\n var label = _ref.label,\n name = _ref.name,\n error = _ref.error,\n hiddenLabels = _ref.hiddenLabels,\n helpIcon = _ref.helpIcon,\n children = _ref.children,\n props = _objectWithoutProperties(_ref, [\"label\", \"name\", \"error\", \"hiddenLabels\", \"helpIcon\", \"children\"]);\n\n var theme = useTheme();\n\n var _useColorMode = useColorMode(),\n colorMode = _useColorMode.colorMode;\n\n var labelColor = colorMode === \"dark\" ? theme.colors.whiteAlpha[600] : theme.colors.blackAlpha[600];\n return __jsx(FormControl, _extends({\n as: Flex,\n flexDirection: \"column\",\n flexGrow: 1,\n flexBasis: 0,\n w: \"100%\",\n maxW: \"100%\",\n mx: 2,\n isInvalid: error && error.message\n }, props), __jsx(FormLabel, {\n htmlFor: name,\n color: labelColor,\n pl: 1,\n opacity: hiddenLabels ? 0 : null\n }, label, (helpIcon === null || helpIcon === void 0 ? void 0 : helpIcon.enable) && __jsx(HelpModal, {\n item: helpIcon,\n name: name\n })), children, __jsx(FormErrorMessage, {\n opacity: hiddenLabels ? 0 : null\n }, error && error.message));\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport { Button, Icon, Tooltip, useClipboard } from \"@chakra-ui/core\";\nexport default (function (_ref) {\n var _ref$bg = _ref.bg,\n bg = _ref$bg === void 0 ? \"secondary\" : _ref$bg,\n copyValue = _ref.copyValue;\n\n var _useClipboard = useClipboard(copyValue),\n onCopy = _useClipboard.onCopy,\n hasCopied = _useClipboard.hasCopied;\n\n return __jsx(Tooltip, {\n hasArrow: true,\n label: \"Copy Output\",\n placement: \"top\"\n }, __jsx(Button, {\n size: \"sm\",\n variantColor: bg,\n zIndex: \"1\",\n onClick: onCopy,\n mx: 1\n }, hasCopied ? __jsx(Icon, {\n name: \"check\",\n size: \"16px\"\n }) : __jsx(Icon, {\n name: \"copy\",\n size: \"16px\"\n })));\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"import _extends from \"@babel/runtime-corejs2/helpers/esm/extends\";\nimport _objectWithoutProperties from \"@babel/runtime-corejs2/helpers/esm/objectWithoutProperties\";\nvar __jsx = React.createElement;\nimport React from \"react\";\nimport { Box, Collapse } from \"@chakra-ui/core\";\nimport MarkDown from \"~/components/MarkDown\";\nexport default React.forwardRef(function (_ref, ref) {\n var _ref$isOpen = _ref.isOpen,\n isOpen = _ref$isOpen === void 0 ? false : _ref$isOpen,\n content = _ref.content,\n _ref$side = _ref.side,\n side = _ref$side === void 0 ? \"left\" : _ref$side,\n title = _ref.title,\n props = _objectWithoutProperties(_ref, [\"isOpen\", \"content\", \"side\", \"title\"]);\n\n return __jsx(Collapse, _extends({\n px: 6,\n py: 4,\n w: \"auto\",\n ref: ref,\n borderBottom: \"1px\",\n display: \"flex\",\n maxWidth: \"100%\",\n isOpen: isOpen,\n flexBasis: \"auto\",\n justifyContent: side === \"left\" ? \"flex-start\" : \"flex-end\"\n }, props), __jsx(Box, {\n textAlign: side\n }, __jsx(MarkDown, {\n content: content\n })));\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport { AccordionItem, AccordionHeader, AccordionPanel, AccordionIcon, Alert, Box, ButtonGroup, Flex, Text, useTheme, useColorMode } from \"@chakra-ui/core\";\nimport styled from \"@emotion/styled\";\nimport useAxios from \"axios-hooks\";\nimport strReplace from \"react-string-replace\";\nimport CopyButton from \"~/components/CopyButton\";\nimport RequeryButton from \"~/components/RequeryButton\";\nimport ResultHeader from \"~/components/ResultHeader\";\nconst PreBox = styled(Box)`\n &::selection {\n background-color: ${props => props.selectionBg};\n color: ${props => props.selectionColor};\n }\n`;\n\nconst FormattedError = ({\n keywords,\n message\n}) => {\n const patternStr = `(${keywords.join(\"|\")})`;\n const pattern = new RegExp(patternStr, \"gi\");\n const errorFmt = strReplace(message, pattern, match => __jsx(Text, {\n as: \"strong\"\n }, match));\n return __jsx(Text, null, errorFmt);\n};\n\nexport default React.forwardRef(({\n config,\n device,\n timeout,\n queryLocation,\n queryType,\n queryVrf,\n queryTarget\n}, ref) => {\n var _error$response, _error$response$data, _error$response2, _error$response2$data, _error$response3, _error$response3$data;\n\n const theme = useTheme();\n const {\n colorMode\n } = useColorMode();\n const bg = {\n dark: theme.colors.gray[800],\n light: theme.colors.blackAlpha[100]\n };\n const color = {\n dark: theme.colors.white,\n light: theme.colors.black\n };\n const selectionBg = {\n dark: theme.colors.white,\n light: theme.colors.black\n };\n const selectionColor = {\n dark: theme.colors.black,\n light: theme.colors.white\n };\n const [{\n data,\n loading,\n error\n }, refetch] = useAxios({\n url: \"/query\",\n method: \"post\",\n data: {\n query_location: queryLocation,\n query_type: queryType,\n query_vrf: queryVrf,\n query_target: queryTarget\n },\n timeout: timeout\n });\n const cleanOutput = data && data.output.split(\"\\\\n\").join(\"\\n\").replace(/\\n\\n/g, \"\");\n const errorKw = error && ((_error$response = error.response) === null || _error$response === void 0 ? void 0 : (_error$response$data = _error$response.data) === null || _error$response$data === void 0 ? void 0 : _error$response$data.keywords) || [];\n const errorMsg = error && ((_error$response2 = error.response) === null || _error$response2 === void 0 ? void 0 : (_error$response2$data = _error$response2.data) === null || _error$response2$data === void 0 ? void 0 : _error$response2$data.output) || error && error.message || config.messages.general;\n return __jsx(AccordionItem, {\n isDisabled: loading,\n ref: ref,\n css: {\n \"&:last-of-type\": {\n borderBottom: \"none\"\n },\n \"&:first-of-type\": {\n borderTop: \"none\"\n }\n }\n }, __jsx(AccordionHeader, {\n justifyContent: \"space-between\"\n }, __jsx(ResultHeader, {\n config: config,\n title: device.display_name,\n loading: loading,\n error: error\n }), __jsx(Flex, null, __jsx(AccordionIcon, null))), __jsx(AccordionPanel, {\n pb: 4\n }, __jsx(Box, {\n position: \"relative\"\n }, data && __jsx(PreBox, {\n fontFamily: \"mono\",\n mt: 5,\n p: 3,\n border: \"1px\",\n borderColor: \"inherit\",\n rounded: \"md\",\n bg: bg[colorMode],\n color: color[colorMode],\n fontSize: \"sm\",\n whiteSpace: \"pre-wrap\",\n as: \"pre\",\n selectionBg: selectionBg[colorMode],\n selectionColor: selectionColor[colorMode]\n }, cleanOutput), error && __jsx(Alert, {\n rounded: \"lg\",\n my: 2,\n py: 4,\n status: ((_error$response3 = error.response) === null || _error$response3 === void 0 ? void 0 : (_error$response3$data = _error$response3.data) === null || _error$response3$data === void 0 ? void 0 : _error$response3$data.alert) || \"error\"\n }, __jsx(FormattedError, {\n keywords: errorKw,\n message: errorMsg\n })), __jsx(ButtonGroup, {\n position: \"absolute\",\n top: 0,\n right: 5,\n py: 3,\n spacing: 4\n }, __jsx(CopyButton, {\n copyValue: cleanOutput\n }), __jsx(RequeryButton, {\n isLoading: loading,\n requery: refetch\n })))));\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n}); // Identify /[param]/ in route string\n\n\nvar TEST_ROUTE = /\\/\\[[^/]+?\\](?=\\/|$)/;\n\nfunction isDynamicRoute(route) {\n return TEST_ROUTE.test(route);\n}\n\nexports.isDynamicRoute = isDynamicRoute;","map":null,"metadata":{},"sourceType":"script"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React, { useEffect, useState } from \"react\";\nimport Head from \"next/head\";\nimport { useTheme } from \"@chakra-ui/core\";\nimport { googleFontUrl } from \"~/util\";\nexport default (({\n config\n}) => {\n const theme = useTheme();\n const {\n 0: location,\n 1: setLocation\n } = useState({});\n const title = (config === null || config === void 0 ? void 0 : config.general.org_name) || \"hyperglass\";\n const description = (config === null || config === void 0 ? void 0 : config.general.site_description) || \"The modern looking glass.\";\n const siteName = `${title} - ${description}`;\n const keywords = (config === null || config === void 0 ? void 0 : config.general.site_keywords) || [\"hyperglass\", \"looking glass\", \"lg\", \"peer\", \"peering\", \"ipv4\", \"ipv6\", \"transit\", \"community\", \"communities\", \"bgp\", \"routing\", \"network\", \"isp\"];\n const author = (config === null || config === void 0 ? void 0 : config.general.org_name) || \"Matt Love, matt@hyperglass.io\";\n const language = (config === null || config === void 0 ? void 0 : config.general.language) || \"en\";\n const currentYear = new Date().getFullYear();\n const copyright = config ? `${currentYear} ${config.general.org_name}` : `${currentYear} hyperglass`;\n const ogImage = (config === null || config === void 0 ? void 0 : config.general.opengraph.image) || null;\n const ogImageHeight = (config === null || config === void 0 ? void 0 : config.general.opengraph.height) || null;\n const ogImageWidth = (config === null || config === void 0 ? void 0 : config.general.opengraph.width) || null;\n const primaryFont = googleFontUrl(theme.fonts.body);\n const monoFont = googleFontUrl(theme.fonts.mono);\n useEffect(() => {\n setLocation(window.location);\n });\n return __jsx(Head, null, __jsx(\"title\", null, title), __jsx(\"meta\", {\n charSet: \"UTF-8\"\n }), __jsx(\"meta\", {\n httpEquiv: \"Content-Type\",\n content: \"text/html\"\n }), __jsx(\"meta\", {\n name: \"description\",\n content: description\n }), __jsx(\"meta\", {\n name: \"keywords\",\n content: keywords.join(\", \")\n }), __jsx(\"meta\", {\n name: \"author\",\n content: author\n }), __jsx(\"meta\", {\n name: \"language\",\n content: language\n }), __jsx(\"meta\", {\n name: \"copyright\",\n content: copyright\n }), __jsx(\"meta\", {\n name: \"url\",\n content: location.href\n }), __jsx(\"meta\", {\n name: \"og:title\",\n content: title\n }), __jsx(\"meta\", {\n name: \"og:type\",\n content: \"website\"\n }), __jsx(\"meta\", {\n name: \"og:site_name\",\n content: siteName\n }), __jsx(\"meta\", {\n name: \"og:url\",\n content: location.href\n }), __jsx(\"meta\", {\n name: \"og:image\",\n content: ogImage\n }), __jsx(\"meta\", {\n name: \"og:description\",\n content: description\n }), __jsx(\"meta\", {\n property: \"og:image:alt\",\n content: siteName\n }), __jsx(\"meta\", {\n property: \"og:image:width\",\n content: ogImageWidth\n }), __jsx(\"meta\", {\n property: \"og:image:height\",\n content: ogImageHeight\n }), __jsx(\"link\", {\n href: primaryFont,\n rel: \"stylesheet\"\n }), __jsx(\"link\", {\n href: monoFont,\n rel: \"stylesheet\"\n }));\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\nvar __importStar = this && this.__importStar || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) {\n if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\n }\n result[\"default\"] = mod;\n return result;\n};\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar React = __importStar(require(\"react\")); // @ts-ignore for some reason the React types don't like this, but it's correct.\n\n\nexports.LoadableContext = React.createContext(null);","map":null,"metadata":{},"sourceType":"script"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport dynamic from \"next/dynamic\";\nimport useAxios from \"axios-hooks\";\nimport { CSSReset, ThemeProvider } from \"@chakra-ui/core\";\nimport Layout from \"~/components/Layout\";\nimport PreConfig from \"~/components/PreConfig\";\nimport { makeTheme, defaultTheme } from \"~/theme\"; // Disable SSR for ColorModeProvider\n\nconst ColorModeProvider = dynamic(() => import(\"@chakra-ui/core\").then(mod => mod.ColorModeProvider), {\n ssr: false,\n loadableGenerated: {\n webpack: () => [require.resolveWeak(\"@chakra-ui/core\")],\n modules: [\"@chakra-ui/core\"]\n }\n});\n\nconst Index = () => {\n const [{\n data,\n loading,\n error\n }, refetch] = useAxios({\n url: \"/config\",\n method: \"get\"\n }); // const data = undefined;\n // const loading = false;\n // const error = { message: \"Shit broke\" };\n // const refetch = () => alert(\"refetched\");\n\n const userTheme = data && makeTheme(data.branding);\n return __jsx(ThemeProvider, {\n theme: data ? userTheme : defaultTheme\n }, __jsx(ColorModeProvider, null, __jsx(CSSReset, null), !data ? __jsx(PreConfig, {\n loading: loading,\n error: error,\n refresh: refetch\n }) : __jsx(Layout, {\n config: data\n })));\n};\n\nexport default Index;","map":null,"metadata":{},"sourceType":"module"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nimport { Flex, IconButton, useColorMode, useTheme } from \"@chakra-ui/core\";\nimport { motion } from \"framer-motion\";\nconst AnimatedFlex = motion.custom(Flex);\nexport default (() => {\n const theme = useTheme();\n const {\n colorMode,\n toggleColorMode\n } = useColorMode();\n const bg = {\n light: theme.colors.white,\n dark: theme.colors.black\n };\n const icon = {\n light: \"moon\",\n dark: \"sun\"\n };\n return __jsx(Flex, {\n position: \"fixed\",\n as: \"header\",\n top: \"0\",\n zIndex: \"4\",\n bg: bg[colorMode],\n color: theme.colors.gray[500],\n left: \"0\",\n right: \"0\",\n width: \"full\",\n height: \"4rem\"\n }, __jsx(Flex, {\n w: \"100%\",\n mx: \"auto\",\n px: 6,\n justifyContent: \"flex-end\"\n }, __jsx(AnimatedFlex, {\n align: \"center\",\n initial: {\n opacity: 0\n },\n animate: {\n opacity: 1\n },\n transition: {\n duration: 0.6\n }\n }, __jsx(IconButton, {\n \"aria-label\": `Switch to ${colorMode === \"light\" ? \"dark\" : \"light\"} mode`,\n variant: \"ghost\",\n color: \"current\",\n ml: \"2\",\n fontSize: \"20px\",\n onClick: toggleColorMode,\n icon: icon[colorMode]\n }))));\n});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n/*\nMIT License\n\nCopyright (c) Jason Miller (https://jasonformat.com/)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\nvar _Object$create = require(\"@babel/runtime-corejs2/core-js/object/create\");\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction mitt() {\n var all = _Object$create(null);\n\n return {\n on: function on(type, handler) {\n ;\n (all[type] || (all[type] = [])).push(handler);\n },\n off: function off(type, handler) {\n if (all[type]) {\n // tslint:disable-next-line:no-bitwise\n all[type].splice(all[type].indexOf(handler) >>> 0, 1);\n }\n },\n emit: function emit(type) {\n for (var _len = arguments.length, evts = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n evts[_key - 1] = arguments[_key];\n }\n\n // eslint-disable-next-line array-callback-return\n ;\n (all[type] || []).slice().map(function (handler) {\n handler.apply(void 0, evts);\n });\n }\n };\n}\n\nexports[\"default\"] = mitt;","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"import chroma from \"chroma-js\";\n\nconst isDark = color => {\n // YIQ equation from http://24ways.org/2010/calculating-color-contrast\n const rgb = chroma(color).rgb();\n const yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n return yiq < 128;\n};\n\nconst isLight = color => isDark(color);\n\nconst opposingColor = (theme, color) => {\n const opposing = isDark(color) ? theme.colors.white : theme.colors.black;\n return opposing;\n};\n\nconst googleFontUrl = (fontFamily, weights = [300, 400, 700]) => {\n const urlWeights = weights.join(\",\");\n const fontName = fontFamily.split(/, /)[0].trim().replace(/'|\"/g, \"\");\n const urlFont = fontName.split(/ /).join(\"+\");\n const urlBase = `https://fonts.googleapis.com/css?family=${urlFont}:${urlWeights}&display=swap`;\n return urlBase;\n};\n\nexport { isDark, isLight, opposingColor, googleFontUrl };","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _Object$defineProperty = require(\"@babel/runtime-corejs2/core-js/object/define-property\");\n\nvar __importStar = this && this.__importStar || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) {\n if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\n }\n result[\"default\"] = mod;\n return result;\n};\n\n_Object$defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar React = __importStar(require(\"react\"));\n\nexports.HeadManagerContext = React.createContext(null);","map":null,"metadata":{},"sourceType":"script"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"var __jsx = React.createElement;\nimport React from \"react\";\nexport default function App({\n Component,\n pageProps\n}) {\n return __jsx(Component, pageProps);\n}\n\nApp.getInitialProps = async () => ({});","map":null,"metadata":{},"sourceType":"module"}

View file

@ -0,0 +1 @@
{"ast":null,"code":"\"use strict\";\n\nvar _classCallCheck = require(\"@babel/runtime-corejs2/helpers/classCallCheck\");\n\nvar _createClass = require(\"@babel/runtime-corejs2/helpers/createClass\");\n\nvar _possibleConstructorReturn = require(\"@babel/runtime-corejs2/helpers/possibleConstructorReturn\");\n\nvar _getPrototypeOf = require(\"@babel/runtime-corejs2/helpers/getPrototypeOf\");\n\nvar _inherits = require(\"@babel/runtime-corejs2/helpers/inherits\");\n\nvar _interopRequireDefault = require(\"@babel/runtime-corejs2/helpers/interopRequireDefault\");\n\nexports.__esModule = true;\nexports[\"default\"] = void 0;\n\nvar _react = _interopRequireDefault(require(\"react\"));\n\nvar _head = _interopRequireDefault(require(\"../next-server/lib/head\"));\n\nvar statusCodes = {\n 400: 'Bad Request',\n 404: 'This page could not be found',\n 405: 'Method Not Allowed',\n 500: 'Internal Server Error'\n};\n/**\n* `Error` component used for handling errors.\n*/\n\nvar Error =\n/*#__PURE__*/\nfunction (_react$default$Compon) {\n _inherits(Error, _react$default$Compon);\n\n function Error() {\n _classCallCheck(this, Error);\n\n return _possibleConstructorReturn(this, _getPrototypeOf(Error).apply(this, arguments));\n }\n\n _createClass(Error, [{\n key: \"render\",\n value: function render() {\n var statusCode = this.props.statusCode;\n var title = this.props.title || statusCodes[statusCode] || 'An unexpected error has occurred';\n return _react[\"default\"].createElement(\"div\", {\n style: styles.error\n }, _react[\"default\"].createElement(_head[\"default\"], null, _react[\"default\"].createElement(\"title\", null, statusCode, \": \", title)), _react[\"default\"].createElement(\"div\", null, _react[\"default\"].createElement(\"style\", {\n dangerouslySetInnerHTML: {\n __html: 'body { margin: 0 }'\n }\n }), statusCode ? _react[\"default\"].createElement(\"h1\", {\n style: styles.h1\n }, statusCode) : null, _react[\"default\"].createElement(\"div\", {\n style: styles.desc\n }, _react[\"default\"].createElement(\"h2\", {\n style: styles.h2\n }, title, \".\"))));\n }\n }], [{\n key: \"getInitialProps\",\n value: function getInitialProps(_ref) {\n var res = _ref.res,\n err = _ref.err;\n var statusCode = res && res.statusCode ? res.statusCode : err ? err.statusCode : 404;\n return {\n statusCode: statusCode\n };\n }\n }]);\n\n return Error;\n}(_react[\"default\"].Component);\n\nexports[\"default\"] = Error;\nError.displayName = 'ErrorPage';\nvar styles = {\n error: {\n color: '#000',\n background: '#fff',\n fontFamily: '-apple-system, BlinkMacSystemFont, Roboto, \"Segoe UI\", \"Fira Sans\", Avenir, \"Helvetica Neue\", \"Lucida Grande\", sans-serif',\n height: '100vh',\n textAlign: 'center',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center'\n },\n desc: {\n display: 'inline-block',\n textAlign: 'left',\n lineHeight: '49px',\n height: '49px',\n verticalAlign: 'middle'\n },\n h1: {\n display: 'inline-block',\n borderRight: '1px solid rgba(0, 0, 0,.3)',\n margin: 0,\n marginRight: '20px',\n padding: '10px 23px 10px 0',\n fontSize: '24px',\n fontWeight: 500,\n verticalAlign: 'top'\n },\n h2: {\n fontSize: '14px',\n fontWeight: 'normal',\n lineHeight: 'inherit',\n margin: 0,\n padding: 0\n }\n};","map":null,"metadata":{},"sourceType":"script"}

Some files were not shown because too many files have changed in this diff Show more