Skip to content

feat: Server compliance audit through a website #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Website

on:
push:
branches:
- main

jobs:
inject-audits:
name: Inject audits
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up node
uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn
- name: Install
run: yarn install --immutable
- name: Build
run: yarn build:umd
- name: Inject
run: node scripts/inject-audits-website.mjs
- name: Upload website
uses: actions/upload-pages-artifact@v1
with:
path: website

deploy:
name: Deploy
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
needs: [inject-audits]
permissions:
pages: write
id-token: write
concurrency:
group: website-deploy
cancel-in-progress: true
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up pages
uses: actions/configure-pages@v3
- name: Deploy
id: deployment
uses: actions/deploy-pages@v1
36 changes: 36 additions & 0 deletions scripts/inject-audits-website.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fs from 'fs/promises';
import path from 'path';

async function main() {
const openMark = '<!-- <graphql-http-audits.min.js> -->';
const closeMark = '<!-- </graphql-http-audits.min.js> -->';

const index = (
await fs.readFile(path.join('website', 'index.html'))
).toString();
const script = (
await fs.readFile(path.join('umd', 'graphql-http-audits.min.js'))
).toString();

await fs.writeFile(
path.join('website', 'index.html'),
index.replace(
new RegExp(`${openMark}.+?${closeMark}`, 's'),
// match the expected indentation in index.html
`${openMark}
<script>
// prettier-ignore
${
// last character is a new line, remove it
script.slice(0, -1)
}
</script>
${closeMark}`,
),
);
}

main().catch((err) => {
console.error(err);
process.exit(1);
});
Binary file added website/favicon.ico
Binary file not shown.
200 changes: 200 additions & 0 deletions website/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="icon" type="image/x-icon" href="favicon.ico" />
<title>graphql-http</title>
<meta
name="description"
content="Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite."
/>

<style>
*,
*:before,
*:after {
box-sizing: border-box;
}
body {
/* use system font */
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol';
}

input {
padding: 0.5em;
font-size: 1em;
width: 100%;
}
button {
cursor: pointer;
font-size: 1em;
}

#audit-server-form {
max-width: 512px;
margin: 0 auto;
}
fieldset {
padding: 1em;
}

#report {
max-width: 1024px;
margin: 2em auto;
padding: 2em;
border: 2px solid grey;
}
#report.hidden {
display: none;
}
#report.auditing {
color: grey;
background-color: lightgrey;
text-align: center;
}
#report.error {
color: red;
border-color: red;
}
li {
margin-bottom: 0.5em;
}
pre {
border: 1px solid grey;
padding: 1em;
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
summary {
cursor: pointer;
}

footer {
text-align: center;
margin-bottom: 2em;
}

.tip {
color: grey;
}
</style>

<!-- <graphql-http-audits.min.js> -->
<!-- the audits script will be injected as a part of the website deployment action -->
<!-- </graphql-http-audits.min.js> -->
</head>
<body>
<main>
<h1 style="text-align: center">
<a href="https://github.com/graphql/graphql-http">graphql-http</a>
</h1>
<p style="text-align: center; color: grey">
Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant
server, client and audit suite.
</p>

<br />

<form id="audit-server-form" name="audit-server">
<fieldset>
<legend>
Check for
<a href="https://graphql.github.io/graphql-over-http/"
>GraphQL over HTTP spec</a
>
compliance
</legend>
<div>
<label for="url">URL of server to audit:</label>
<br />
<input id="url" type="url" name="url" required autofocus />
<br />
<small
>Please make sure that
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"
>CORS</a
>
allows this origin on your server.</small
>
</div>
<br />
<div style="text-align: right">
<button type="submit">Audit</button>
</div>
</fieldset>
</form>

<div id="report" class="hidden"></div>

<p style="text-align: center">
<small class="tip"
><b>💡 Tip:</b> Save this website and use it as a portable offline
compliance checker.</small
>
</p>

<footer>
<small>
<a href="https://roadmap.sh/graphql">Learn GraphQL</a>
|
<a href="https://graphql.github.io/graphql-over-http/"
>GraphQL over HTTP spec</a
>
|
<a href="https://bundlephobia.com/package/graphql-http"
>Bundlephobia (package size)</a
>
|
<a href="https://github.com/graphql/graphql-http">GitHub</a>
|
<a href="https://www.npmjs.com/package/graphql-http">npm</a>
</small>
</footer>
</main>

<script>
const params = new URLSearchParams(window.location.search);
const url = params.get('url');
if (url) {
const urlInput = document.getElementById('url');
urlInput.setAttribute('value', url);
urlInput.setAttribute('disabled', true);

const reportDiv = document.getElementById('report');
reportDiv.classList.remove('hidden');
reportDiv.classList.add('auditing');
reportDiv.innerHTML = '⏳ Auditing...';

graphqlHttpAudits
.auditServer({ url })
.then((results) =>
graphqlHttpAudits
.renderAuditResultsToHTML(results)
.then((html) => (reportDiv.innerHTML = html)),
)
.catch((err) => {
console.error('Problem while auditing server', err);
reportDiv.classList.add('error');
reportDiv.innerHTML = `
<small class="tip"><b>💡 Tip:</b> Open the console to see more details about the error.</small>
<br /><br />

<pre style="border: 0; padding: 0">${
err instanceof Error
? err.message + '\n\n' + err.stack
: JSON.stringify(err, null, ' ')
}</pre>`;
})
.finally(() => {
urlInput.removeAttribute('disabled');
reportDiv.classList.remove('auditing');
});
}
</script>
</body>
</html>