Copy forward-headers from nginx to local node-fetch. Fixes #73

This commit is contained in:
Dessalines 2020-11-12 15:56:46 -06:00
parent ebe60406be
commit e86cd3eddb
18 changed files with 206 additions and 189 deletions

View file

@ -1,4 +1,3 @@
// import cookieParser = require('cookie-parser');
import serialize from 'serialize-javascript';
import express from 'express';
import { StaticRouter } from 'inferno-router';
@ -6,14 +5,16 @@ import { renderToString } from 'inferno-server';
import { matchPath } from 'inferno-router';
import path from 'path';
import { App } from '../shared/components/app';
import { IsoData } from '../shared/interfaces';
import { InitialFetchRequest, IsoData } from '../shared/interfaces';
import { routes } from '../shared/routes';
import IsomorphicCookie from 'isomorphic-cookie';
import { lemmyHttp, setAuth } from '../shared/utils';
import { GetSiteForm } from 'lemmy-js-client';
import { setAuth } from '../shared/utils';
import { GetSiteForm, LemmyHttp } from 'lemmy-js-client';
import process from 'process';
import { Helmet } from 'inferno-helmet';
import { initializeSite } from '../shared/initialize';
import { httpUri } from '../shared/env';
import { IncomingHttpHeaders } from 'http';
const server = express();
const port = 1234;
@ -34,12 +35,20 @@ server.get('/*', async (req, res) => {
let promises: Promise<any>[] = [];
let headers = setForwardedHeaders(req.headers);
let initialFetchReq: InitialFetchRequest = {
client: new LemmyHttp(httpUri, headers),
auth,
path: req.path,
};
// Get site data first
let site = await lemmyHttp.getSite(getSiteForm);
let site = await initialFetchReq.client.getSite(getSiteForm);
initializeSite(site);
if (activeRoute.fetchInitialData) {
promises.push(...activeRoute.fetchInitialData(auth, req.path));
promises.push(...activeRoute.fetchInitialData(initialFetchReq));
}
let routeData = await Promise.all(promises);
@ -119,16 +128,25 @@ server.get('/*', async (req, res) => {
</html>
`);
});
let Server = server.listen(port, () => {
server.listen(port, () => {
console.log(`http://localhost:${port}`);
});
/**
* Used to restart server by fuseBox
*/
export async function shutdown() {
Server.close();
Server = undefined;
function setForwardedHeaders(
headers: IncomingHttpHeaders
): { [key: string]: string } {
let out = {
host: headers.host,
};
if (headers['x-real-ip']) {
out['x-real-ip'] = headers['x-real-ip'];
}
if (headers['x-forwarded-for']) {
out['x-forwarded-for'] = headers['x-forwarded-for'];
}
return out;
}
process.on('SIGINT', () => {

View file

@ -18,7 +18,6 @@ import {
setIsoData,
wsSubscribe,
isBrowser,
lemmyHttp,
setAuth,
} from '../utils';
import autosize from 'autosize';
@ -26,6 +25,7 @@ import { SiteForm } from './site-form';
import { UserListing } from './user-listing';
import { HtmlTags } from './html-tags';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
interface AdminSettingsState {
siteRes: GetSiteResponse;
@ -71,10 +71,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
}
}
static fetchInitialData(auth: string, _path: string): Promise<any>[] {
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let form: GetSiteConfig = {};
setAuth(form, auth);
return [lemmyHttp.getSiteConfig(form)];
setAuth(form, req.auth);
return [req.client.getSiteConfig(form)];
}
componentDidMount() {

View file

@ -18,13 +18,13 @@ import {
toast,
getPageFromProps,
isBrowser,
lemmyHttp,
setAuth,
setIsoData,
wsSubscribe,
} from '../utils';
import { CommunityLink } from './community-link';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
const communityLimit = 100;
@ -237,17 +237,17 @@ export class Communities extends Component<any, CommunitiesState> {
WebSocketService.Instance.listCommunities(listCommunitiesForm);
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let page = pathSplit[3] ? Number(pathSplit[3]) : 1;
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType.TopAll,
limit: communityLimit,
page,
};
setAuth(listCommunitiesForm, auth);
setAuth(listCommunitiesForm, req.auth);
return [lemmyHttp.listCommunities(listCommunitiesForm)];
return [req.client.listCommunities(listCommunitiesForm)];
}
parseMessage(msg: WebSocketJsonResponse) {

View file

@ -1,6 +1,6 @@
import { Component, linkEvent } from 'inferno';
import { Subscription } from 'rxjs';
import { DataType } from '../interfaces';
import { DataType, InitialFetchRequest } from '../interfaces';
import {
UserOperation,
GetCommunityResponse,
@ -50,7 +50,6 @@ import {
setIsoData,
wsSubscribe,
isBrowser,
lemmyHttp,
setAuth,
communityRSSUrl,
} from '../utils';
@ -150,8 +149,8 @@ export class Community extends Component<any, State> {
};
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let promises: Promise<any>[] = [];
// It can be /c/main, or /c/1
@ -165,8 +164,8 @@ export class Community extends Component<any, State> {
}
let communityForm: GetCommunityForm = id ? { id } : { name: name_ };
setAuth(communityForm, auth);
promises.push(lemmyHttp.getCommunity(communityForm));
setAuth(communityForm, req.auth);
promises.push(req.client.getCommunity(communityForm));
let dataType: DataType = pathSplit[4]
? DataType[pathSplit[4]]
@ -188,8 +187,8 @@ export class Community extends Component<any, State> {
type_: ListingType.Community,
};
this.setIdOrName(getPostsForm, id, name_);
setAuth(getPostsForm, auth);
promises.push(lemmyHttp.getPosts(getPostsForm));
setAuth(getPostsForm, req.auth);
promises.push(req.client.getPosts(getPostsForm));
} else {
let getCommentsForm: GetCommentsForm = {
page,
@ -198,11 +197,11 @@ export class Community extends Component<any, State> {
type_: ListingType.Community,
};
this.setIdOrName(getCommentsForm, id, name_);
setAuth(getCommentsForm, auth);
promises.push(lemmyHttp.getComments(getCommentsForm));
setAuth(getCommentsForm, req.auth);
promises.push(req.client.getComments(getCommentsForm));
}
promises.push(lemmyHttp.listCategories());
promises.push(req.client.listCategories());
return promises;
}

View file

@ -16,10 +16,10 @@ import {
wsJsonToRes,
wsSubscribe,
isBrowser,
lemmyHttp,
} from '../utils';
import { WebSocketService, UserService } from '../services';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
interface CreateCommunityState {
site: Site;
@ -100,8 +100,8 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
this.props.history.push(`/c/${community.name}`);
}
static fetchInitialData(_auth: string, _path: string): Promise<any>[] {
return [lemmyHttp.listCategories()];
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
return [req.client.listCategories()];
}
parseMessage(msg: WebSocketJsonResponse) {

View file

@ -4,7 +4,6 @@ import { PostForm } from './post-form';
import { HtmlTags } from './html-tags';
import {
isBrowser,
lemmyHttp,
setAuth,
setIsoData,
toast,
@ -23,6 +22,7 @@ import {
SortType,
} from 'lemmy-js-client';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
interface CreatePostState {
site: Site;
@ -138,13 +138,13 @@ export class CreatePost extends Component<any, CreatePostState> {
this.props.history.push(`/post/${id}`);
}
static fetchInitialData(auth: string, _path: string): Promise<any>[] {
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let listCommunitiesForm: ListCommunitiesForm = {
sort: SortType.TopAll,
limit: 9999,
};
setAuth(listCommunitiesForm, auth);
return [lemmyHttp.listCommunities(listCommunitiesForm)];
setAuth(listCommunitiesForm, req.auth);
return [req.client.listCommunities(listCommunitiesForm)];
}
parseMessage(msg: WebSocketJsonResponse) {

View file

@ -15,7 +15,6 @@ import {
import {
getRecipientIdFromProps,
isBrowser,
lemmyHttp,
setAuth,
setIsoData,
toast,
@ -23,6 +22,7 @@ import {
wsSubscribe,
} from '../utils';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
interface CreatePrivateMessageProps {}
@ -78,15 +78,15 @@ export class CreatePrivateMessage extends Component<
WebSocketService.Instance.getUserDetails(form);
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let user_id = Number(path.split('/').pop());
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let user_id = Number(req.path.split('/').pop());
let form: GetUserDetailsForm = {
user_id,
sort: SortType.New,
saved_only: false,
};
setAuth(form, auth);
return [lemmyHttp.getUserDetails(form)];
setAuth(form, req.auth);
return [req.client.getUserDetails(form)];
}
get documentTitle(): string {

View file

@ -30,7 +30,6 @@ import {
setupTippy,
setIsoData,
wsSubscribe,
lemmyHttp,
setAuth,
isBrowser,
} from '../utils';
@ -39,6 +38,7 @@ import { PrivateMessage } from './private-message';
import { HtmlTags } from './html-tags';
import { SortSelect } from './sort-select';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
enum UnreadOrAll {
Unread,
@ -404,7 +404,7 @@ export class Inbox extends Component<any, InboxState> {
i.refetch();
}
static fetchInitialData(auth: string, _path: string): Promise<any>[] {
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let promises: Promise<any>[] = [];
// It can be /u/me, or /username/1
@ -414,8 +414,8 @@ export class Inbox extends Component<any, InboxState> {
page: 1,
limit: fetchLimit,
};
setAuth(repliesForm, auth);
promises.push(lemmyHttp.getReplies(repliesForm));
setAuth(repliesForm, req.auth);
promises.push(req.client.getReplies(repliesForm));
let userMentionsForm: GetUserMentionsForm = {
sort: SortType.New,
@ -423,16 +423,16 @@ export class Inbox extends Component<any, InboxState> {
page: 1,
limit: fetchLimit,
};
setAuth(userMentionsForm, auth);
promises.push(lemmyHttp.getUserMentions(userMentionsForm));
setAuth(userMentionsForm, req.auth);
promises.push(req.client.getUserMentions(userMentionsForm));
let privateMessagesForm: GetPrivateMessagesForm = {
unread_only: true,
page: 1,
limit: fetchLimit,
};
setAuth(privateMessagesForm, auth);
promises.push(lemmyHttp.getPrivateMessages(privateMessagesForm));
setAuth(privateMessagesForm, req.auth);
promises.push(req.client.getPrivateMessages(privateMessagesForm));
return promises;
}

View file

@ -24,7 +24,7 @@ import {
BanUserResponse,
WebSocketJsonResponse,
} from 'lemmy-js-client';
import { DataType } from '../interfaces';
import { DataType, InitialFetchRequest } from '../interfaces';
import { WebSocketService, UserService } from '../services';
import { PostListings } from './post-listings';
import { CommentNodes } from './comment-nodes';
@ -56,7 +56,6 @@ import {
wsSubscribe,
isBrowser,
setAuth,
lemmyHttp,
} from '../utils';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
@ -175,8 +174,8 @@ export class Main extends Component<any, MainState> {
};
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let dataType: DataType = pathSplit[3]
? DataType[pathSplit[3]]
: DataType.Post;
@ -206,8 +205,8 @@ export class Main extends Component<any, MainState> {
sort,
type_,
};
setAuth(getPostsForm, auth);
promises.push(lemmyHttp.getPosts(getPostsForm));
setAuth(getPostsForm, req.auth);
promises.push(req.client.getPosts(getPostsForm));
} else {
let getCommentsForm: GetCommentsForm = {
page,
@ -215,18 +214,18 @@ export class Main extends Component<any, MainState> {
sort,
type_,
};
setAuth(getCommentsForm, auth);
promises.push(lemmyHttp.getComments(getCommentsForm));
setAuth(getCommentsForm, req.auth);
promises.push(req.client.getComments(getCommentsForm));
}
let trendingCommunitiesForm: ListCommunitiesForm = {
sort: SortType.Hot,
limit: 6,
};
promises.push(lemmyHttp.listCommunities(trendingCommunitiesForm));
promises.push(req.client.listCommunities(trendingCommunitiesForm));
if (auth) {
promises.push(lemmyHttp.getFollowedCommunities({ auth }));
if (req.auth) {
promises.push(req.client.getFollowedCommunities({ auth: req.auth }));
}
return promises;

View file

@ -26,12 +26,12 @@ import {
setIsoData,
wsSubscribe,
isBrowser,
lemmyHttp,
} from '../utils';
import { MomentTime } from './moment-time';
import { HtmlTags } from './html-tags';
import moment from 'moment';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
interface ModlogState {
combined: {
@ -443,8 +443,8 @@ export class Modlog extends Component<any, ModlogState> {
WebSocketService.Instance.getModlog(modlogForm);
}
static fetchInitialData(_auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let communityId = pathSplit[3];
let promises: Promise<any>[] = [];
@ -457,7 +457,7 @@ export class Modlog extends Component<any, ModlogState> {
modlogForm.community_id = Number(communityId);
}
promises.push(lemmyHttp.getModlog(modlogForm));
promises.push(req.client.getModlog(modlogForm));
return promises;
}

View file

@ -25,7 +25,11 @@ import {
ListCategoriesResponse,
Category,
} from 'lemmy-js-client';
import { CommentSortType, CommentViewType } from '../interfaces';
import {
CommentSortType,
CommentViewType,
InitialFetchRequest,
} from '../interfaces';
import { WebSocketService, UserService } from '../services';
import {
wsJsonToRes,
@ -41,7 +45,6 @@ import {
getCommentIdFromProps,
wsSubscribe,
setAuth,
lemmyHttp,
isBrowser,
previewLines,
isImage,
@ -112,8 +115,8 @@ export class Post extends Component<any, PostState> {
WebSocketService.Instance.getPost(form);
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let promises: Promise<any>[] = [];
let id = Number(pathSplit[2]);
@ -121,10 +124,10 @@ export class Post extends Component<any, PostState> {
let postForm: GetPostForm = {
id,
};
setAuth(postForm, auth);
setAuth(postForm, req.auth);
promises.push(lemmyHttp.getPost(postForm));
promises.push(lemmyHttp.listCategories());
promises.push(req.client.getPost(postForm));
promises.push(req.client.listCategories());
return promises;
}

View file

@ -27,7 +27,6 @@ import {
commentsToFlatNodes,
setIsoData,
wsSubscribe,
lemmyHttp,
setAuth,
} from '../utils';
import { PostListing } from './post-listing';
@ -37,6 +36,7 @@ import { CommunityLink } from './community-link';
import { SortSelect } from './sort-select';
import { CommentNodes } from './comment-nodes';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
interface SearchProps {
q: string;
@ -132,8 +132,8 @@ export class Search extends Component<any, SearchState> {
};
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let promises: Promise<any>[] = [];
let form: SearchForm = {
@ -143,10 +143,10 @@ export class Search extends Component<any, SearchState> {
page: this.getPageFromProps(pathSplit[9]),
limit: fetchLimit,
};
setAuth(form, auth);
setAuth(form, req.auth);
if (form.q != '') {
promises.push(lemmyHttp.search(form));
promises.push(req.client.search(form));
}
return promises;

View file

@ -17,7 +17,7 @@ import {
PostResponse,
BanUserResponse,
} from 'lemmy-js-client';
import { UserDetailsView } from '../interfaces';
import { InitialFetchRequest, UserDetailsView } from '../interfaces';
import { WebSocketService, UserService } from '../services';
import {
wsJsonToRes,
@ -41,7 +41,6 @@ import {
saveCommentRes,
createPostLikeFindRes,
setAuth,
lemmyHttp,
previewLines,
editPostFindRes,
} from '../utils';
@ -187,8 +186,8 @@ export class User extends Component<any, UserState> {
return page ? Number(page) : 1;
}
static fetchInitialData(auth: string, path: string): Promise<any>[] {
let pathSplit = path.split('/');
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let pathSplit = req.path.split('/');
let promises: Promise<any>[] = [];
// It can be /u/me, or /username/1
@ -212,8 +211,8 @@ export class User extends Component<any, UserState> {
limit: fetchLimit,
};
this.setIdOrName(form, user_id, username);
setAuth(form, auth);
promises.push(lemmyHttp.getUserDetails(form));
setAuth(form, req.auth);
promises.push(req.client.getUserDetails(form));
return promises;
}

View file

@ -1,4 +1,4 @@
import { GetSiteResponse } from 'lemmy-js-client';
import { GetSiteResponse, LemmyHttp } from 'lemmy-js-client';
export interface IsoData {
path: string;
@ -15,6 +15,12 @@ declare global {
}
}
export interface InitialFetchRequest {
auth: string;
path: string;
client: LemmyHttp;
}
export enum CommentSortType {
Hot,
Top,

View file

@ -15,9 +15,10 @@ import { AdminSettings } from './components/admin-settings';
import { Inbox } from './components/inbox';
import { Search } from './components/search';
import { Instances } from './components/instances';
import { InitialFetchRequest } from './interfaces';
interface IRoutePropsWithFetch extends IRouteProps {
fetchInitialData?(auth: string, path: string): Promise<any>[];
fetchInitialData?(req: InitialFetchRequest): Promise<any>[];
}
export const routes: IRoutePropsWithFetch[] = [
@ -25,12 +26,12 @@ export const routes: IRoutePropsWithFetch[] = [
path: `/`,
exact: true,
component: Main,
fetchInitialData: (auth, path) => Main.fetchInitialData(auth, path),
fetchInitialData: req => Main.fetchInitialData(req),
},
{
path: `/home/data_type/:data_type/listing_type/:listing_type/sort/:sort/page/:page`,
component: Main,
fetchInitialData: (auth, path) => Main.fetchInitialData(auth, path),
fetchInitialData: req => Main.fetchInitialData(req),
},
{
path: `/login`,
@ -39,101 +40,98 @@ export const routes: IRoutePropsWithFetch[] = [
{
path: `/create_post`,
component: CreatePost,
fetchInitialData: (auth, path) => CreatePost.fetchInitialData(auth, path),
fetchInitialData: req => CreatePost.fetchInitialData(req),
},
{
path: `/create_community`,
component: CreateCommunity,
fetchInitialData: (auth, path) =>
CreateCommunity.fetchInitialData(auth, path),
fetchInitialData: req => CreateCommunity.fetchInitialData(req),
},
{
path: `/create_private_message/recipient/:recipient_id`,
component: CreatePrivateMessage,
fetchInitialData: (auth, path) =>
CreatePrivateMessage.fetchInitialData(auth, path),
fetchInitialData: req => CreatePrivateMessage.fetchInitialData(req),
},
{
path: `/communities/page/:page`,
component: Communities,
fetchInitialData: (auth, path) => Communities.fetchInitialData(auth, path),
fetchInitialData: req => Communities.fetchInitialData(req),
},
{
path: `/communities`,
component: Communities,
fetchInitialData: (auth, path) => Communities.fetchInitialData(auth, path),
fetchInitialData: req => Communities.fetchInitialData(req),
},
{
path: `/post/:id/comment/:comment_id`,
component: Post,
fetchInitialData: (auth, path) => Post.fetchInitialData(auth, path),
fetchInitialData: req => Post.fetchInitialData(req),
},
{
path: `/post/:id`,
component: Post,
fetchInitialData: (auth, path) => Post.fetchInitialData(auth, path),
fetchInitialData: req => Post.fetchInitialData(req),
},
{
path: `/c/:name/data_type/:data_type/sort/:sort/page/:page`,
component: Community,
fetchInitialData: (auth, path) => Community.fetchInitialData(auth, path),
fetchInitialData: req => Community.fetchInitialData(req),
},
{
path: `/community/:id`,
component: Community,
fetchInitialData: (auth, path) => Community.fetchInitialData(auth, path),
fetchInitialData: req => Community.fetchInitialData(req),
},
{
path: `/c/:name`,
component: Community,
fetchInitialData: (auth, path) => Community.fetchInitialData(auth, path),
fetchInitialData: req => Community.fetchInitialData(req),
},
{
path: `/u/:username/view/:view/sort/:sort/page/:page`,
component: User,
fetchInitialData: (auth, path) => User.fetchInitialData(auth, path),
fetchInitialData: req => User.fetchInitialData(req),
},
{
path: `/user/:id`,
component: User,
fetchInitialData: (auth, path) => User.fetchInitialData(auth, path),
fetchInitialData: req => User.fetchInitialData(req),
},
{
path: `/u/:username`,
component: User,
fetchInitialData: (auth, path) => User.fetchInitialData(auth, path),
fetchInitialData: req => User.fetchInitialData(req),
},
{
path: `/inbox`,
component: Inbox,
fetchInitialData: (auth, path) => Inbox.fetchInitialData(auth, path),
fetchInitialData: req => Inbox.fetchInitialData(req),
},
{
path: `/modlog/community/:community_id`,
component: Modlog,
fetchInitialData: (auth, path) => Modlog.fetchInitialData(auth, path),
fetchInitialData: req => Modlog.fetchInitialData(req),
},
{
path: `/modlog`,
component: Modlog,
fetchInitialData: (auth, path) => Modlog.fetchInitialData(auth, path),
fetchInitialData: req => Modlog.fetchInitialData(req),
},
{ path: `/setup`, component: Setup },
{
path: `/admin`,
component: AdminSettings,
fetchInitialData: (auth, path) =>
AdminSettings.fetchInitialData(auth, path),
fetchInitialData: req => AdminSettings.fetchInitialData(req),
},
{
path: `/search/q/:q/type/:type/sort/:sort/page/:page`,
component: Search,
fetchInitialData: (auth, path) => Search.fetchInitialData(auth, path),
fetchInitialData: req => Search.fetchInitialData(req),
},
{
path: `/search`,
component: Search,
fetchInitialData: (auth, path) => Search.fetchInitialData(auth, path),
fetchInitialData: req => Search.fetchInitialData(req),
},
{
path: `/password_change/:token`,

View file

@ -43,11 +43,8 @@ import {
SearchResponse,
CommentResponse,
PostResponse,
LemmyHttp,
} from 'lemmy-js-client';
import { httpUri } from './env';
import { CommentSortType, DataType, IsoData } from './interfaces';
import { UserService, WebSocketService } from './services';
@ -84,8 +81,6 @@ export const postRefetchSeconds: number = 60 * 1000;
export const fetchLimit: number = 20;
export const mentionDropdownFetchLimit = 10;
export const lemmyHttp = new LemmyHttp(httpUri);
export const languages = [
{ code: 'ca', name: 'Català' },
{ code: 'en', name: 'English' },