run prettier

main
Damien Erambert 2022-07-31 21:54:56 +02:00
parent af47228724
commit d2d41e089e
No known key found for this signature in database
GPG Key ID: 519179F777AE5A0F
2 changed files with 214 additions and 195 deletions

376
lib.js
View File

@ -1,17 +1,16 @@
const _fetch = require('node-fetch')
const _fetch = require("node-fetch");
const crypto = require("crypto");
const needle = require('needle');
const needle = require("needle");
const { decode } = require("./lib/b64arraybuffer");
const fs = require('fs');
const path = require('path');
const mime = require('mime-types');
const fs = require("fs");
const path = require("path");
const mime = require("mime-types");
const API_BASE = "https://cohost.org/api/v1";
/**
* Fetches an API endpoint.
*
*
* @private
* @param {string} method HTTP method to use
* @param {string} endpoint Relative endpoint to fetch
@ -20,97 +19,104 @@ const API_BASE = "https://cohost.org/api/v1";
* @param {boolean} [complex=false] Whether to return {headers, body}, or just the body
* @returns Response, JSON parsed if parsable, string if not
*/
async function fetch(method, endpoint, cookies = "", data, complex = false, headers = {}) {
let url = API_BASE + endpoint + (method == "GET" && data ? "?" + new URLSearchParams(data).toString() : "");
async function fetch(
method,
endpoint,
cookies = "",
data,
complex = false,
headers = {}
) {
let url =
API_BASE +
endpoint +
(method == "GET" && data ? "?" + new URLSearchParams(data).toString() : "");
let req = await _fetch(url, {
method,
headers: {
"Content-Type": "application/json",
"Cookie": cookies,
},
body: (method != "GET" && data) ? JSON.stringify(data) : undefined
});
let req = await _fetch(url, {
method,
headers: {
"Content-Type": "application/json",
Cookie: cookies
},
body: method != "GET" && data ? JSON.stringify(data) : undefined
});
let res = await req.text();
try {
res = JSON.parse(res);
} catch (_) {}
let res = await req.text();
try {
res = JSON.parse(res);
} catch (_) {}
if (req.status >= 400) {
throw JSON.stringify(res);
if (req.status >= 400) {
throw JSON.stringify(res);
} else {
if (complex) {
return {
headers: req.headers,
body: res
};
} else {
if (complex) {
return {
headers: req.headers,
body: res
};
} else {
return res;
}
return res;
}
}
}
/**
* Represents a cohost User (e.g. john.doe@gmail.com)
*/
class User {
/**
* Authenticates the User.
* This should always be called before using this instance or its references.
*
* @param {string} email E-mail address
* @param {string} password Password
*/
async login(email, password) {
const { salt } = await fetch(
"GET",
"/login/salt",
undefined,
{ email }
);
const hash = crypto.pbkdf2Sync(Buffer.from(password, "utf8"), decode(salt), 200000, 128, "sha384")
const clientHash = Buffer.from(hash).toString("base64");
/**
* Authenticates the User.
* This should always be called before using this instance or its references.
*
* @param {string} email E-mail address
* @param {string} password Password
*/
async login(email, password) {
const { salt } = await fetch("GET", "/login/salt", undefined, { email });
const res = await fetch(
"POST",
"/login",
undefined,
{ email, clientHash },
true
);
const hash = crypto.pbkdf2Sync(
Buffer.from(password, "utf8"),
decode(salt),
200000,
128,
"sha384"
);
const clientHash = Buffer.from(hash).toString("base64");
this.sessionCookie = res.headers.get("set-cookie").split(";")[0];
const res = await fetch(
"POST",
"/login",
undefined,
{ email, clientHash },
true
);
this.userId = res.body.userId;
this.email = res.body.email;
}
this.sessionCookie = res.headers.get("set-cookie").split(";")[0];
/**
* Get Projects the User has edit permissions on.
*
* @returns {Project[]} User's projects
*/
async getProjects() {
return (await fetch(
"GET",
"/projects/edited",
this.sessionCookie
)).projects.map(x => new Project(this, x));
}
this.userId = res.body.userId;
this.email = res.body.email;
}
/**
* Get Notifications of the User. Docs TBD
*/
async getNotifications(offset = 0, limit = 20) {
return (await fetch(
"GET",
"/notifications/list",
this.sessionCookie,
{ offset, limit }
))
}
/**
* Get Projects the User has edit permissions on.
*
* @returns {Project[]} User's projects
*/
async getProjects() {
return (
await fetch("GET", "/projects/edited", this.sessionCookie)
).projects.map(x => new Project(this, x));
}
/**
* Get Notifications of the User. Docs TBD
*/
async getNotifications(offset = 0, limit = 20) {
return await fetch("GET", "/notifications/list", this.sessionCookie, {
offset,
limit
});
}
}
/**
@ -160,7 +166,7 @@ class Project {
this.user.sessionCookie
);
return res.items.map((x) => new Post(this.user, x));
return res.items.map(x => new Post(this.user, x));
}
async uploadAttachment(postId, filename) {
@ -175,27 +181,32 @@ class Project {
{
filename: path.basename(filename),
content_type: fileContentType,
content_length: fileContentLength,
content_length: fileContentLength
}
);
await needle('post', S3Parameters.url, {
...S3Parameters.requiredFields,
file: {
file: filename,
content_type: fileContentType
}
}, {multipart: true});
await needle(
"post",
S3Parameters.url,
{
...S3Parameters.requiredFields,
file: {
file: filename,
content_type: fileContentType
}
},
{ multipart: true }
);
const res = fetch(
const res = fetch(
"POST",
`/project/${encodeURIComponent(
this.handle
)}/posts/${postId}/attach/finish/${S3Parameters.attachmentId}`,
this.user.sessionCookie
)
);
console.log(await res)
console.log(await res);
}
}
@ -203,103 +214,104 @@ class Project {
* Represents a cohost Post
*/
class Post {
constructor(user, data) {
this.user = user;
this.populate(data);
}
constructor(user, data) {
this.user = user;
this.populate(data);
}
/**
* @typedef {Object} PostMarkdownBlock
* @property {string} content
*/
/**
* @typedef {Object} PostMarkdownBlock
* @property {string} content
*/
/**
* @typedef {Object} PostAttachmentBlock
* @property {string} fileURL
* @property {string} attachmentId
*/
/**
* @typedef {Object} PostAttachmentBlock
* @property {string} fileURL
* @property {string} attachmentId
*/
/**
* @typedef {Object} PostBlock
* @property {string} type Type of block. Currently available: 'markdown' and 'attachment'
* @property {PostMarkdownBlock} [markdown] Should only be present if type is 'markdown'
* @property {PostAttachmentBlock} [attachment] Should only be present if type is 'attachment'
*/
/**
* @typedef {Object} PostBlock
* @property {string} type Type of block. Currently available: 'markdown' and 'attachment'
* @property {PostMarkdownBlock} [markdown] Should only be present if type is 'markdown'
* @property {PostAttachmentBlock} [attachment] Should only be present if type is 'attachment'
*/
/**
* @typedef {Object} PostCreate
* @property {number} postState 1 for published, 0 for draft? or pending maybe?
* @property {string} headline Headline
* @property {boolean} adultContent Does the post contain adult content?
* @property {PostBlock[]} blocks Blocks (docs TBD)
* @property {string[]} cws Content Warnings
* @property {string[]} tags Tags (docs TBD)
*/
/**
* @typedef {Object} PostCreate
* @property {number} postState 1 for published, 0 for draft? or pending maybe?
* @property {string} headline Headline
* @property {boolean} adultContent Does the post contain adult content?
* @property {PostBlock[]} blocks Blocks (docs TBD)
* @property {string[]} cws Content Warnings
* @property {string[]} tags Tags (docs TBD)
*/
/**
*
* @param {Project} project Project to post to
* @param {PostCreate} data
* @returns
*/
static async create(project, data) {
let { postId } = await fetch(
"POST",
`/project/${encodeURIComponent(project.handle)}/posts`,
project.user.sessionCookie,
data
);
/**
*
* @param {Project} project Project to post to
* @param {PostCreate} data
* @returns
*/
static async create(project, data) {
let { postId } = await fetch(
"POST",
`/project/${encodeURIComponent(project.handle)}/posts`,
project.user.sessionCookie,
data
);
// return await Post.getById(project, postId);
return postId;
}
// return await Post.getById(project, postId);
return postId;
}
// Endpoint is disabled;
// static async getById(project, id) {
// let data = await fetch(
// "GET",
// `/project_posts/${id}`,
// project.user.sessionCookie
// );
// Endpoint is disabled;
// static async getById(project, id) {
// let data = await fetch(
// "GET",
// `/project_posts/${id}`,
// project.user.sessionCookie
// );
// return new Post(user, data);
// }
// return new Post(user, data);
// }
/**
* @private
*/
populate(data) {
this.id = data.postId;
this.headline = data.headline;
this.publishedAt = new Date(data.publishedAt);
this.filename = data.filename;
this.transparentShareOfPostId = data.transparentShareOfPostId;
this.state = data.state;
this.numComments = data.numComments;
this.numSharedComments = data.numSharedComments;
this.cws = data.cws;
this.tags = data.tags;
this.blocks = data.blocks;
this.plainTextBody = data.plainTextBody;
this.project = new Project(this.user, data.postingProject);
this.shareTree = data.shareTree;
this.relatedProjects = data.relatedProjects;
this.effectiveAdultContent = data.effectiveAdultContent;
this.isEditor = data.isEditor;
this.contributorBlockIncomingOrOutgoing = data.contributorBlockIncomingOrOutgoing;
this.hasAnyContributorMuted = data.hasAnyContributorMuted;
this.isLiked = data.isLiked;
this.canShare = data.canShare;
this.canPublish = data.canPublish;
this.singlePostPageUrl = data.singlePostPageUrl;
this.renderInIframe = data.renderInIframe;
this.postPreviewIFrameUrl = data.postPreviewIFrameUrl;
this.postEditUrl = data.postEditUrl;
}
/**
* @private
*/
populate(data) {
this.id = data.postId;
this.headline = data.headline;
this.publishedAt = new Date(data.publishedAt);
this.filename = data.filename;
this.transparentShareOfPostId = data.transparentShareOfPostId;
this.state = data.state;
this.numComments = data.numComments;
this.numSharedComments = data.numSharedComments;
this.cws = data.cws;
this.tags = data.tags;
this.blocks = data.blocks;
this.plainTextBody = data.plainTextBody;
this.project = new Project(this.user, data.postingProject);
this.shareTree = data.shareTree;
this.relatedProjects = data.relatedProjects;
this.effectiveAdultContent = data.effectiveAdultContent;
this.isEditor = data.isEditor;
this.contributorBlockIncomingOrOutgoing =
data.contributorBlockIncomingOrOutgoing;
this.hasAnyContributorMuted = data.hasAnyContributorMuted;
this.isLiked = data.isLiked;
this.canShare = data.canShare;
this.canPublish = data.canPublish;
this.singlePostPageUrl = data.singlePostPageUrl;
this.renderInIframe = data.renderInIframe;
this.postPreviewIFrameUrl = data.postPreviewIFrameUrl;
this.postEditUrl = data.postEditUrl;
}
}
module.exports = {
User,
Project,
Post,
};
User,
Project,
Post
};

View File

@ -8,28 +8,35 @@ for (var i = 0; i < chars.length; i++) {
function encode(arraybuffer) {
var bytes = new Uint8Array(arraybuffer),
i, len = bytes.length, base64 = "";
i,
len = bytes.length,
base64 = "";
for (i = 0; i < len; i+=3) {
for (i = 0; i < len; i += 3) {
base64 += chars[bytes[i] >> 2];
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
base64 += chars[bytes[i + 2] & 63];
}
if ((len % 3) === 2) {
if (len % 3 === 2) {
base64 = base64.substring(0, base64.length - 1) + "=";
} else if (len % 3 === 1) {
base64 = base64.substring(0, base64.length - 2) + "==";
}
return base64;
};
}
function decode(base64) {
var bufferLength = base64.length * 0.75,
len = base64.length, i, p = 0,
encoded1, encoded2, encoded3, encoded4;
len = base64.length,
i,
p = 0,
encoded1,
encoded2,
encoded3,
encoded4;
if (base64[base64.length - 1] === "=") {
bufferLength--;
@ -39,13 +46,13 @@ function decode(base64) {
}
var arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);
bytes = new Uint8Array(arraybuffer);
for (i = 0; i < len; i+=4) {
for (i = 0; i < len; i += 4) {
encoded1 = lookup[base64.charCodeAt(i)];
encoded2 = lookup[base64.charCodeAt(i+1)];
encoded3 = lookup[base64.charCodeAt(i+2)];
encoded4 = lookup[base64.charCodeAt(i+3)];
encoded2 = lookup[base64.charCodeAt(i + 1)];
encoded3 = lookup[base64.charCodeAt(i + 2)];
encoded4 = lookup[base64.charCodeAt(i + 3)];
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
@ -53,6 +60,6 @@ function decode(base64) {
}
return arraybuffer;
};
}
module.exports = {encode, decode};
module.exports = { encode, decode };