Skip to content

Commit

Permalink
feat: website (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fyko committed Feb 27, 2022
1 parent dceb96c commit 09e2162
Show file tree
Hide file tree
Showing 28 changed files with 23,389 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Deploy Website to GitHub Pages

on:
push:
branches: [main]

jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
defaults:
run:
working-directory: website
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
cache: npm

- name: Build website
run: |
npm ci
npm run build
# Popular action to deploy to GitHub Pages:
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Build output to publish to the `gh-pages` branch:
publish_dir: ./build
# Assign commit authorship to the official GH-Actions bot for deploys to `gh-pages` branch:
# https://github.com/actions/checkout/issues/13#issuecomment-724415212
# The GH actions bot is used by default if you didn't specify the two fields.
# You can swap them out with your own user credentials.
user_name: github-actions[bot]
user_email: 41898282+github-actions[bot]@users.noreply.github.com
20 changes: 20 additions & 0 deletions website/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
41 changes: 41 additions & 0 deletions website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Website

This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.

### Installation

```
$ yarn
```

### Local Development

```
$ yarn start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build

```
$ yarn build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment

Using SSH:

```
$ USE_SSH=true yarn deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
3 changes: 3 additions & 0 deletions website/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
5 changes: 5 additions & 0 deletions website/docs/api-versions/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"label": "API Versions",
"position": 2
}

152 changes: 152 additions & 0 deletions website/docs/api-versions/gRPC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
id: gRPC
title: gRPC (reccomended)
sidebar_position: 0
---

v3 of the Export API introduces a gRPC API. One of the major upsides of using the gRPC protocol is a stream of progress updates and finally streaming the file back to the client.

The `@fyko/export-api` package includes a typed JavaScript client and will be referenced in this part of the documentation.

## Protobuf Definition

:::tip Server Reflection
The gRPC service has [server reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md), allowing you to test the API in Postman.
:::

```protobuf
service Exporter {
rpc CreateExport (CreateExportRequest) returns (stream CreateExportResponse);
}
enum ExportFormat {
PlainText = 0;
HtmlDark = 1;
HtmlLight = 2;
CSV = 3;
JSON = 4;
}
message CreateExportRequest {
string token = 1;
string channel_id = 2;
ExportFormat export_format = 3;
string date_format = 4;
string after = 5;
string before = 6;
}
message CreateExportResponse {
oneof ResponseType {
double progress = 1;
ExportComplete data = 2;
}
}
message ExportComplete {
int32 message_count = 1;
bytes data = 2;
}
```

## `Exporter/CreateExport`

> `rpc CreateExport (CreateExportRequest) returns (stream CreateExportResponse);`
### Export Formats Enum

| **Type** | **ID** | **Description** | **File Extension** |
| --------- | ------ | --------------------------------------- | ------------------ |
| PlainText | 0 | Export to a plaintext file | txt |
| HtmlDark | 1 | Export to an HTML file in dark mode | html |
| HtmlLight | 2 | Export to an HTML file in light mode | html |
| CSV | 3 | Export to a comma separated values file | csv |
| JSON | 4 | Export to a JSON file | json |

### CreateExportRequest

| **Field** | **Type** | **Description** |
| ------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| token | string | The bot token for performing requests |
| channel_id | string | The id of the channel to export |
| export_format | ?[ExportFormat](#export-formats-enum) | The format to export the channel as, defaults to `PlainText` |
| date_format | ?string | The [date format](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings) for dates in exported files, defaults to `dd-MMM-yy hh:mm tt` |
| after | ?string | Only include messages sent after this date |
| before | ?string | Only include messages sent before this date |

### CreateExportResponse

| **Field** | **Type** | **Description** |
| --------- | ---------------------------------- | ------------------------------------------------- |
| progress | int64 | A decimal representing the progress of the export |
| data | ?[ExportComplete](#exportcomplete) | The file data once `progress` equals `1` |

### ExportComplete

| **Field** | **Type** | **Description** |
| ------------- | -------- | -------------------------------- |
| message_count | int | The number of messages exported |
| data | byte[] | The exported file in 32kb chunks |

### Example

```ts
import { credentials } from "@grpc/grpc-js";
import { ExporterClient } from "@fyko/export-api/client";
import {
CreateExportRequest,
CreateExportResponse,
ExportFormat,
} from "@fyko/export-api/types";
import { writeFile } from "fs/promises";

// creates a new gRPC client
const client = new ExporterClient(
`localhost:${process.env.PORT}`,
credentials.createInsecure()
);

void (async () => {
// new CreateExport Request
const request = new CreateExportRequest();
// set required options
request.setChannelId(process.env.DISCORD_CHANNEL!);
request.setToken(process.env.DISCORD_TOKEN!);
// set optional options
request.setExportFormat(ExportFormat.HTMLDARK);

//
return new Promise(async (res, rej) => {
// "POST" the request
const stream = client.createExport(request);

const chunks: (string | Uint8Array)[] = [];
let progress = 0;
stream.on("data", (response: CreateExportResponse) => {
// if `response.progress` is present
const p = response.getProgress();
if (p && p > progress) {
progress = p;
console.log((p * 100).toFixed() + "%");
}

// if finally sending the file itself, push to chunk array
const data = response.getData();
const inner = data?.getData();
if (inner) {
console.log(`Inner exists!`);
chunks.push(inner);
}
});

// once the server closes the stream,
// we can finally write the file
stream.on("end", async () => {
await writeFile("./foo.html", chunks);
return res(void 0);
});

stream.on("error", rej);
});
})();
```
67 changes: 67 additions & 0 deletions website/docs/api-versions/v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
id: v1
title: v1
sidebar_position: 0
---

:::danger API Deprecated
This API version is deprecated, it's advised you use either [v2](v2.md) or [gRPC](gRPC.md).
:::

## `POST` `/v1/export`
__JSON Body__

| **Field** | **Type** | **Description** |
|-----------|----------|---------------------------------------|
| token | string | The bot token for performing requests |
| channelId | string | The id of the channel to export |

__Response Codes__

| **Status** | **Description** |
|------------|----------------------------------------------|
| 200 | Success - exported channel sent as text/html |
| 401 | Unauthorized - bad Discord bot token |
| 409 | Conflict - unknown channel |

### Examples
#### Typescript:
```ts
import fetch from 'node-fetch';

async function exportChannel(channelId: string, token: string): Promise<Buffer> {
const response = await fetch('http://exportapi:80/v1/export', {
method: 'POST',
body: JSON.stringify({ channelId, token }),
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
return response.buffer();
}
throw Error('Channel export failed!');
}
```
#### Rust
```rust
// reqwest = { version = "0.10", features = ["json"] }
use reqwest::Client;
use std::collections::HashMap;
use std::io::copy;
use std::fs::File;

async fn export_channel(channelId: &str, token: &str) -> Result<File, reqwest::Error> {
let client = Client::new();
let mut map = HashMap::new();
map.insert("channelId", "channel id");
map.insert("token", "discord token");

let file = client.post("http://exportapi:80/v1/export").json(&map).await?.text().await?;

let dest = File::create("myexport.html")?;
copy(&mut file.as_bytes(), &mut dest)?;

Ok(dest)
}
```
Loading

0 comments on commit 09e2162

Please sign in to comment.