Skip to content

Commit

Permalink
v0.3 WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
areknawo committed Oct 6, 2023
1 parent 09b1832 commit 2baaae7
Show file tree
Hide file tree
Showing 174 changed files with 4,479 additions and 3,108 deletions.
50 changes: 26 additions & 24 deletions apps/backend/api/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
createOpenApiNodeHttpHandler,
CreateOpenApiNodeHttpHandlerOptions
} from "trpc-openapi/dist/adapters/node-http/core";
import corsPlugin from "@fastify/cors";
import corsPlugin, { OriginFunction } from "@fastify/cors";
import { OpenApiRouter } from "trpc-openapi";
import { AnyRouter } from "@trpc/server";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
Expand Down Expand Up @@ -55,36 +55,38 @@ const fastifyTRPCOpenApiPlugin = <TRouter extends AnyRouter>(
done();
};
const apiService = publicPlugin(async (fastify) => {
const originCallback: OriginFunction = (origin, callback) => {
if (!origin || origin === "null") {
callback(null, true);

return;
}

const { hostname } = new URL(origin);
const appHostname = new URL(fastify.config.PUBLIC_APP_URL).hostname;

if (
hostname === "localhost" ||
hostname.endsWith(appHostname) ||
(fastify.config.VRITE_CLOUD && hostname.endsWith("swagger.io"))
) {
callback(null, true);

return;
}

callback(new Error("Not allowed"), false);
};

await fastify.register(rateLimitPlugin, {
max: 500,
timeWindow: "1 minute",
redis: fastify.redis
});
await fastify.register(corsPlugin, {
origin: true,
credentials: true,
methods: ["GET", "DELETE", "PUT", "POST"],
origin(origin, callback) {
if (!origin || origin === "null") {
callback(null, true);

return;
}

const { hostname } = new URL(origin);
const appHostname = new URL(fastify.config.PUBLIC_APP_URL).hostname;

if (
hostname === "localhost" ||
hostname.endsWith(appHostname) ||
(fastify.config.VRITE_CLOUD && hostname.endsWith("swagger.io"))
) {
callback(null, true);

return;
}

callback(new Error("Not allowed"), false);
}
methods: ["GET", "DELETE", "PUT", "POST"]
});
await fastify.register(fastifyTRPCOpenApiPlugin, {
basePath: "/",
Expand Down
11 changes: 9 additions & 2 deletions apps/backend/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import { generateOpenApiDocument } from "trpc-openapi";
import { createServer, appRouter } from "@vrite/backend";

(async () => {
const server = await createServer();
const server = await createServer({
database: true,
pubSub: true,
auth: true,
email: true,
gitSync: true,
search: true
});

await server.register(apiService);
server.get("/swagger.json", (req, res) => {
res.send(
generateOpenApiDocument(appRouter, {
baseUrl: server.config.PUBLIC_API_URL,
title: "Vrite API",
version: "0.2.0"
version: "0.3.0"
})
);
});
Expand Down
56 changes: 2 additions & 54 deletions apps/backend/app/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { appRouter, errors, publicPlugin, trpcPlugin } from "@vrite/backend";
import { errors, publicPlugin, trpcPlugin, processAuth } from "@vrite/backend";
import staticPlugin from "@fastify/static";
import websocketPlugin from "@fastify/websocket";
import axios from "axios";
import viewPlugin from "@fastify/view";
import handlebars from "handlebars";
import { FastifyReply } from "fastify";
import { processAuth } from "@vrite/backend/src/lib/auth";
import { nanoid } from "nanoid";
import multipartPlugin from "@fastify/multipart";
import mime from "mime-types";
Expand All @@ -15,7 +14,7 @@ import path from "path";

const appService = publicPlugin(async (fastify) => {
const renderPage = async (reply: FastifyReply): Promise<void> => {
return reply.view("index.html", {
return reply.header("X-Frame-Options", "SAMEORIGIN").view("index.html", {
PUBLIC_APP_URL: fastify.config.PUBLIC_APP_URL,
PUBLIC_API_URL: fastify.config.PUBLIC_API_URL,
PUBLIC_COLLAB_URL: fastify.config.PUBLIC_COLLAB_URL,
Expand Down Expand Up @@ -51,57 +50,6 @@ const appService = publicPlugin(async (fastify) => {
fastify.setNotFoundHandler(async (_request, reply) => {
return renderPage(reply);
});
fastify.get<{ Querystring: { url: string } }>("/proxy*", async (request, reply) => {
const filterOutRegex =
/(localhost|\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)(?::\d{0,4})?\b)/;

if (request.headers.origin) {
reply.header("Access-Control-Allow-Origin", fastify.config.PUBLIC_APP_URL);
reply.header("Access-Control-Allow-Methods", "GET");
reply.header(
"Access-Control-Allow-Headers",
request.headers["access-control-request-headers"]
);
} else if (
fastify.config.NODE_ENV !== "development" &&
!fastify.config.PUBLIC_APP_URL.includes("localhost")
) {
// Prevent proxy abuse in production
return reply.status(400).send("Invalid Origin");
}

if (
filterOutRegex.test(request.query.url) &&
!request.query.url.includes(fastify.config.PUBLIC_ASSETS_URL)
) {
return reply.status(400).send("Invalid URL");
}

if (request.method === "OPTIONS") {
// CORS Preflight
reply.send();
} else {
const targetURL = request.query.url;

try {
const response = await axios.get(targetURL, {
responseType: "arraybuffer"
});

if (!`${response.headers["content-type"]}`.includes("image")) {
return reply.status(400).send("Invalid Content-Type");
}

reply.header("content-type", response.headers["content-type"]);
reply.send(Buffer.from(response.data, "binary"));
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);

return reply.status(500).send("Could not fetch");
}
}
});
fastify.post<{
Body: Buffer;
}>("/upload", async (req, res) => {
Expand Down
10 changes: 9 additions & 1 deletion apps/backend/app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import { appService } from "./app";
import { createServer } from "@vrite/backend";

(async () => {
const server = await createServer();
const server = await createServer({
database: true,
pubSub: true,
auth: true,
email: true,
gitSync: true,
search: true,
storage: true
});

await server.register(appService);
server.listen({ host: server.config.HOST, port: server.config.PORT }, (err) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ const assetsService = publicPlugin(async (fastify) => {
await reply.header("Content-Type", sourceContentType).send(sourceAsset);
};

reply.header("Access-Control-Allow-Origin", fastify.config.PUBLIC_APP_URL);
reply.header(
"Access-Control-Allow-Origin",
fastify.config.NODE_ENV === "development" ? "*" : fastify.config.PUBLIC_APP_URL
);
reply.header("Access-Control-Allow-Methods", "GET");

if (!sourceAsset) return reply.status(404).send();
Expand Down
6 changes: 4 additions & 2 deletions apps/backend/assets/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { assetsService } from "./api";
import { assetsService } from "./assets";
import { createServer } from "@vrite/backend";

(async () => {
const server = await createServer();
const server = await createServer({
storage: true
});

await server.register(assetsService);

Expand Down
18 changes: 4 additions & 14 deletions apps/backend/collaboration/src/extensions/git-sync.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { Extension, onChangePayload, onDisconnectPayload } from "@hocuspocus/server";
import {
GitData,
ObjectId,
createGenericOutputContentProcessor,
docToJSON,
getContentPiecesCollection,
getGitDataCollection,
jsonToBuffer
jsonToBuffer,
publishGitDataEvent
} from "@vrite/backend";
import { createEventPublisher } from "@vrite/backend/src/lib/pub-sub";
import { FastifyInstance } from "fastify";
import { ObjectId } from "mongodb";
import crypto from "node:crypto";

interface Configuration {
debounce: number | false | null;
debounceMaxWait: number;
}

type GitDataEvent = {
action: "update";
data: Partial<GitData>;
};

class GitSync implements Extension {
private configuration: Configuration = {
debounce: 5000,
Expand All @@ -36,10 +30,6 @@ class GitSync implements Extension {

private debounced: Map<string, { timeout: NodeJS.Timeout; start: number }> = new Map();

private publishGitDataEvent = createEventPublisher<GitDataEvent>((workspaceId) => {
return `gitData:${workspaceId}`;
});

public constructor(fastify: FastifyInstance, configuration?: Partial<Configuration>) {
this.fastify = fastify;
this.configuration = {
Expand Down Expand Up @@ -121,7 +111,7 @@ class GitSync implements Extension {
}
}
);
this.publishGitDataEvent({ fastify: this.fastify }, `${details.context.workspaceId}`, {
publishGitDataEvent({ fastify: this.fastify }, `${details.context.workspaceId}`, {
action: "update",
data: {
records: gitData.records.map((record: any) => {
Expand Down
3 changes: 2 additions & 1 deletion apps/backend/collaboration/src/extensions/search-indexing.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Extension, onChangePayload, onDisconnectPayload } from "@hocuspocus/server";
import { ObjectId, docToBuffer, getContentPiecesCollection } from "@vrite/backend";
import { docToBuffer, getContentPiecesCollection } from "@vrite/backend";
import { FastifyInstance } from "fastify";
import { ObjectId } from "mongodb";

interface Configuration {
debounce: number | false | null;
Expand Down
7 changes: 6 additions & 1 deletion apps/backend/collaboration/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { writingPlugin } from "./writing";
import { createServer } from "@vrite/backend";

(async () => {
const server = await createServer();
const server = await createServer({
database: true,
auth: true,
pubSub: true,
search: true
});

await server.register(writingPlugin);
})();
14 changes: 9 additions & 5 deletions apps/backend/collaboration/src/writing.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { publicPlugin, getContentsCollection, getContentVariantsCollection } from "@vrite/backend";
import {
publicPlugin,
getContentsCollection,
getContentVariantsCollection,
errors,
SessionData
} from "@vrite/backend";
import { Server } from "@hocuspocus/server";
import { Database } from "@hocuspocus/extension-database";
import { Redis } from "@hocuspocus/extension-redis";
import { ObjectId, Binary } from "mongodb";
import { SessionData } from "@vrite/backend/src/lib/session";
import { unauthorized } from "@vrite/backend/src/lib/errors";
import { SearchIndexing } from "#extensions/search-indexing";
import { GitSync } from "#extensions/git-sync";

Expand All @@ -18,13 +22,13 @@ const writingPlugin = publicPlugin(async (fastify) => {
const cookies = fastify.parseCookie(data.requestHeaders.cookie || "");

if (!cookies.accessToken) {
throw unauthorized();
throw errors.unauthorized();
}

const token = fastify.unsignCookie(cookies.accessToken || "")?.value || "";

if (!token) {
throw unauthorized();
throw errors.unauthorized();
}

const { sessionId } = fastify.jwt.verify<{ sessionId: string }>(token);
Expand Down
7 changes: 6 additions & 1 deletion apps/backend/extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
"dependencies": {
"@fastify/cors": "^8.3.0",
"@trpc/server": "^10.35.0",
"@types/mdast": "^4.0.1",
"@vrite/backend": "workspace:*",
"@vrite/sdk": "workspace:*",
"fastify": "^4.20.0",
"openai": "^4.0.0",
"trpc-openapi": "^1.2.0"
"remark": "^15.0.1",
"remark-mdx": "^2.3.0",
"remark-parse": "^11.0.0",
"trpc-openapi": "^1.2.0",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@vrite/scripts": "workspace:*"
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/extensions/src/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
createOpenApiNodeHttpHandler,
CreateOpenApiNodeHttpHandlerOptions
} from "trpc-openapi/dist/adapters/node-http/core";
import corsPlugin from "@fastify/cors";
import { OpenApiRouter } from "trpc-openapi";
import { AnyRouter } from "@trpc/server";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import corsPlugin from "@fastify/cors";

type CreateOpenApiFastifyPluginOptions<TRouter extends OpenApiRouter> =
CreateOpenApiNodeHttpHandlerOptions<TRouter, any, any> & {
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/extensions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { extensionsService } from "./extensions";
import { createServer, z } from "@vrite/backend";

(async () => {
const server = await createServer();
const server = await createServer({});

await server.register(extensionsService);

Expand Down
6 changes: 3 additions & 3 deletions apps/backend/extensions/src/routes/dev/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const devOutputTransformer = createOutputTransformer<string>((contentNode) => {
};
const transformContentNode = (
nodeWalker: JSONContentNodeWalker<
JSONContentNode["listItem" | "blockquote" | "doc" | "wrapper"]
JSONContentNode["listItem" | "blockquote" | "doc" | "element"]
>
): string => {
return nodeWalker.children
Expand Down Expand Up @@ -150,9 +150,9 @@ const devOutputTransformer = createOutputTransformer<string>((contentNode) => {
return `\n\`\`\`${child.node.attrs?.lang || ""}\n${transformTextNode(
child as JSONContentNodeWalker<JSONContentNode["codeBlock"]>
)}\n\`\`\`\n`;
case "wrapper":
case "element":
return `\n${transformContentNode(
child as JSONContentNodeWalker<JSONContentNode["wrapper"]>
child as JSONContentNodeWalker<JSONContentNode["element"]>
)}\n`;
case "blockquote":
return `\n${transformContentNode(
Expand Down
Loading

0 comments on commit 2baaae7

Please sign in to comment.