Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.env
node_modules/
files/
dev_notes.txt
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,37 @@ To link states, just name a state, and in a button of another state, write the s
```

# Installing and running
Create a .env file containing postgresql credential
You'll need to install postgres.

After you have it installed, create a .env file containing postgresql credential
```
PG_USER=postgres_user
PG_PASSWORD=postgres_password
PG_HOST=postgres_host
PG_USER=uxmd
PG_PASSWORD=1u2x3m4d5
PG_HOST=localhost
PG_PORT=5432
PG_DATABASE=database_name
NODE_ENV=development
PG_DATABASE=uxmd
```

Connect to your PostgreSQL database. If you need to set up your user:
```psql
CREATE USER uxmd PASSWORD '1u2x3m4d5';
CREATE DATABASE uxmd;
GRANT ALL ON DATABASE uxmd TO uxmd;
```

Connect to your PostgreSQL database and create the Documents table
Create the Documents table
```
CREATE TABLE Documents(id SERIAL PRIMARY KEY, route VARCHAR, body VARCHAR);
```

Install all the dependencies using
```
npm install
yarn install
```

Then you can run the local dev server using
```
npm run dev
yarn dev
```

It will use nodemon, which autoreload the server when you save changes to files (it will not reload the page tho)
1 change: 0 additions & 1 deletion create_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ module.exports = async (route) => {
<head>
<title>UXMD</title>
<meta charset="UTF-8" />
<meta http-equiv="Refresh" content="1.5; url='/${route}'" />
<link rel="icon" href="public/favicon.svg" />
<link rel="stylesheet" href="public/style.css" />
<script defer data-domain="uxmd.io" src="https://plausible.io/js/plausible.js"></script>
Expand Down
4 changes: 2 additions & 2 deletions error_page.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = async () => {
module.exports = async (httpCode=404, message="") => {
return `
<!DOCTYPE html>
<html lang="en">
Expand All @@ -11,7 +11,7 @@ module.exports = async () => {
<script defer data-domain="uxmd.io" src="https://plausible.io/js/plausible.js"></script>
</head>
<body>
<div id="error_not_found">404<a href="/">Go Back</a></div>
<div id="error_not_found">${httpCode} ${message}<a href="/">Go Back</a></div>
</body>
</html>`;
};
71 changes: 71 additions & 0 deletions landing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const querystring = require("querystring")

const REDIRECT_ENDPOINT = 'github_auth'

const getGithubRedirectUrl = () => {
if(process.env.UXMD_ENV == "development") {
return `http://localhost:${process.env.PORT}/${REDIRECT_ENDPOINT}`
} else {
return `https://www.uxmd.io/${REDIRECT_ENDPOINT}`
}
}

const getGithubUrl = (state) => {
const host = "https://github.com/login/oauth/authorize"
const query = querystring.stringify({
client_id: process.env.GITHUB_CLIENT_ID,
redirect_uri: getGithubRedirectUrl(),
state: state,
scope: "user"
})
return `${host}?${query}`
}

const renderLoginOrDashboard = (state) => {
return `<script>
const session = localStorage.getItem("__uxmd_session")
const parent = document.getElementById("contents")
if(session) {
const page = document.createElement("div")
page.innerHTML = "Have session"
parent.appendChild(page)
} else {
const page = document.createElement("a")
page.setAttribute("href", "${getGithubUrl(state)}")
page.innerHTML = "Login with GitHub"
parent.appendChild(page)
}
</script>`

}

module.exports = async (state) => {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<title>UXMD</title>
<meta charset="UTF-8" />
<link rel="icon" href="public/favicon.svg" />
<link rel="stylesheet" href="public/style.css" />
<script defer data-domain="uxmd.io" src="https://plausible.io/js/plausible.js"></script>
</head>
<body>
<div id="loader">
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M58 22L48 32M48 12L58 22L48 12ZM68 32L58 22L68 32ZM58 22L68 12L58 22Z" stroke="#FFCB70" stroke-width="8" stroke-linecap="round" stroke-linejoin="round">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" dur="1.5s" from="0 58 22" to="360 58 22" repeatCount="indefinite"></animateTransform>
</path>
<g stroke="#444" stroke-width="8" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 12V24C12 29.5228 16.4772 34 22 34C27.5228 34 32 29.5228 32 24V12"/>
<path d="M32 68V48L22 58L12 48V68"/>
<path d="M58 48H48V68H58C63.5228 68 68 63.5228 68 58C68 52.4772 63.5228 48 58 48Z"/>
</g>
</svg>
<div id="contents"/>
${renderLoginOrDashboard(state)}
</div>
</body>
</html>
`
};
113 changes: 113 additions & 0 deletions lib/http_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const https = require('https')
const querystring = require("querystring")

const HEADERS_JSON = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}

const HEADERS_URL_ENCODED = {
'Content-Type': 'application/x-www-form-urlencoded'
}

class HttpClient {
constructor(host, port, certificateAuthority) {
this.host = host
this.port = port
this.certificateAuthority = certificateAuthority
}

async postJson(endpoint, data, headers={}) {
headers = Object.assign(headers, HEADERS_JSON)
return JSON.parse(await this.postString(endpoint, JSON.stringify(data), headers))
}

async postFormUrlEncoded(endpoint, data, headers={}) {
headers = Object.assign(headers, HEADERS_URL_ENCODED)
let xFormBody = ""
for(const key of Object.keys(data)) {
xFormBody = xFormBody + `${key}=${data[key]}&`
}
xFormBody = xFormBody.substring(0, xFormBody.length - 1)
return this.postString(endpoint, xFormBody, headers)
}

async postString(endpoint, dataString, headers) {
const options = {
host: this.host,
port: this.port,
path: endpoint,
method: 'POST',
ca: this.certificateAuthority,
headers: headers,
}
return this.request(options, dataString)
}

async getJson(endpoint, data, headers={}) {
headers = Object.assign(headers, HEADERS_JSON)
const stringifiedData = data ? querystring.stringify(data) : ""
const uriParams = stringifiedData.length > 0 ? `?${stringifiedData}` : ""
const path = endpoint + uriParams
return this.get(path, headers)
}

async paginatedGetJson(endpoint, data, headers={}, offset=0) {
Object.assign(data, { offset })
let response = await this.getJson(endpoint, data, headers)
const result = response.items || []
while(response.next) {
response = await this.get(response.next, headers)
result = result.concat(response.items)
}
return result
}

async get(path, headers) {
const options = {
host: this.host,
port: this.port,
path: path,
method: 'GET',
headers: headers,
ca: this.certificateAuthority
}
/* No data for GET requests*/
return (await this.request(options, null)).toString()
}

async request(options, bytes) {
delete options.port
delete options.ca
console.log("REQUESTING", options, bytes)

let responseChunks = []
return new Promise((resolve, reject) => {
const request = https.request(options, (res) => {
res.on('data', (d) => {
responseChunks.push(d)
})
res.on('end', () => {
try {
resolve(Buffer.concat(responseChunks))
} catch(e) {
reject(e)
}

})
res.on('error', (e) => {
reject(e)
})
})
request.on('error', (e) => {
reject(e)
})
if(bytes) {
request.write(bytes)
}
request.end()
})
}
}

module.exports = HttpClient
19 changes: 19 additions & 0 deletions lib/pg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { Pool } = require("pg");

const init = () => {
const connectionString = `postgresql://${process.env.PG_USER}:${process.env.PG_PASSWORD}@${process.env.PG_HOST}:${process.env.PG_PORT}/${process.env.PG_DATABASE}`;

const isHeroku = process.env.UXMD_ENV === "production";
const config = {
connectionString: isHeroku ? process.env.DATABASE_URL : connectionString,
}
if(isHeroku) {
config.ssl = {
rejectUnauthorized: false
}
}
return new Pool(config);
}

module.exports = init

Loading