Skip to content

Commit 1877683

Browse files
authored
Vrite v0.3 (#45)
- Replace wrapper with element; - Add XML-like syntax for elements; - Redesign editor block menus; - Add MDX extension; - Allow CORS API access; - Restructure @vrite/backend;
1 parent 09b1832 commit 1877683

File tree

224 files changed

+8327
-5720
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

224 files changed

+8327
-5720
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ yarn-error.log*
2626
.pnpm-debug.log*
2727

2828
# local env files
29+
docker.env
2930
.env
3031
.env.local
3132
.env.development.local

Diff for: apps/backend/api/src/api.ts

+26-24
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
createOpenApiNodeHttpHandler,
55
CreateOpenApiNodeHttpHandlerOptions
66
} from "trpc-openapi/dist/adapters/node-http/core";
7-
import corsPlugin from "@fastify/cors";
7+
import corsPlugin, { OriginFunction } from "@fastify/cors";
88
import { OpenApiRouter } from "trpc-openapi";
99
import { AnyRouter } from "@trpc/server";
1010
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
@@ -55,36 +55,38 @@ const fastifyTRPCOpenApiPlugin = <TRouter extends AnyRouter>(
5555
done();
5656
};
5757
const apiService = publicPlugin(async (fastify) => {
58+
const originCallback: OriginFunction = (origin, callback) => {
59+
if (!origin || origin === "null") {
60+
callback(null, true);
61+
62+
return;
63+
}
64+
65+
const { hostname } = new URL(origin);
66+
const appHostname = new URL(fastify.config.PUBLIC_APP_URL).hostname;
67+
68+
if (
69+
hostname === "localhost" ||
70+
hostname.endsWith(appHostname) ||
71+
(fastify.config.VRITE_CLOUD && hostname.endsWith("swagger.io"))
72+
) {
73+
callback(null, true);
74+
75+
return;
76+
}
77+
78+
callback(new Error("Not allowed"), false);
79+
};
80+
5881
await fastify.register(rateLimitPlugin, {
5982
max: 500,
6083
timeWindow: "1 minute",
6184
redis: fastify.redis
6285
});
6386
await fastify.register(corsPlugin, {
87+
origin: true,
6488
credentials: true,
65-
methods: ["GET", "DELETE", "PUT", "POST"],
66-
origin(origin, callback) {
67-
if (!origin || origin === "null") {
68-
callback(null, true);
69-
70-
return;
71-
}
72-
73-
const { hostname } = new URL(origin);
74-
const appHostname = new URL(fastify.config.PUBLIC_APP_URL).hostname;
75-
76-
if (
77-
hostname === "localhost" ||
78-
hostname.endsWith(appHostname) ||
79-
(fastify.config.VRITE_CLOUD && hostname.endsWith("swagger.io"))
80-
) {
81-
callback(null, true);
82-
83-
return;
84-
}
85-
86-
callback(new Error("Not allowed"), false);
87-
}
89+
methods: ["GET", "DELETE", "PUT", "POST"]
8890
});
8991
await fastify.register(fastifyTRPCOpenApiPlugin, {
9092
basePath: "/",

Diff for: apps/backend/api/src/index.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@ import { generateOpenApiDocument } from "trpc-openapi";
33
import { createServer, appRouter } from "@vrite/backend";
44

55
(async () => {
6-
const server = await createServer();
6+
const server = await createServer({
7+
database: true,
8+
pubSub: true,
9+
auth: true,
10+
email: true,
11+
gitSync: true,
12+
search: true
13+
});
714

815
await server.register(apiService);
916
server.get("/swagger.json", (req, res) => {
1017
res.send(
1118
generateOpenApiDocument(appRouter, {
1219
baseUrl: server.config.PUBLIC_API_URL,
1320
title: "Vrite API",
14-
version: "0.2.0"
21+
version: "0.3.0"
1522
})
1623
);
1724
});

Diff for: apps/backend/app/src/app.ts

+2-54
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { appRouter, errors, publicPlugin, trpcPlugin } from "@vrite/backend";
1+
import { errors, publicPlugin, trpcPlugin, processAuth } from "@vrite/backend";
22
import staticPlugin from "@fastify/static";
33
import websocketPlugin from "@fastify/websocket";
44
import axios from "axios";
55
import viewPlugin from "@fastify/view";
66
import handlebars from "handlebars";
77
import { FastifyReply } from "fastify";
8-
import { processAuth } from "@vrite/backend/src/lib/auth";
98
import { nanoid } from "nanoid";
109
import multipartPlugin from "@fastify/multipart";
1110
import mime from "mime-types";
@@ -15,7 +14,7 @@ import path from "path";
1514

1615
const appService = publicPlugin(async (fastify) => {
1716
const renderPage = async (reply: FastifyReply): Promise<void> => {
18-
return reply.view("index.html", {
17+
return reply.header("X-Frame-Options", "SAMEORIGIN").view("index.html", {
1918
PUBLIC_APP_URL: fastify.config.PUBLIC_APP_URL,
2019
PUBLIC_API_URL: fastify.config.PUBLIC_API_URL,
2120
PUBLIC_COLLAB_URL: fastify.config.PUBLIC_COLLAB_URL,
@@ -51,57 +50,6 @@ const appService = publicPlugin(async (fastify) => {
5150
fastify.setNotFoundHandler(async (_request, reply) => {
5251
return renderPage(reply);
5352
});
54-
fastify.get<{ Querystring: { url: string } }>("/proxy*", async (request, reply) => {
55-
const filterOutRegex =
56-
/(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)/;
57-
58-
if (request.headers.origin) {
59-
reply.header("Access-Control-Allow-Origin", fastify.config.PUBLIC_APP_URL);
60-
reply.header("Access-Control-Allow-Methods", "GET");
61-
reply.header(
62-
"Access-Control-Allow-Headers",
63-
request.headers["access-control-request-headers"]
64-
);
65-
} else if (
66-
fastify.config.NODE_ENV !== "development" &&
67-
!fastify.config.PUBLIC_APP_URL.includes("localhost")
68-
) {
69-
// Prevent proxy abuse in production
70-
return reply.status(400).send("Invalid Origin");
71-
}
72-
73-
if (
74-
filterOutRegex.test(request.query.url) &&
75-
!request.query.url.includes(fastify.config.PUBLIC_ASSETS_URL)
76-
) {
77-
return reply.status(400).send("Invalid URL");
78-
}
79-
80-
if (request.method === "OPTIONS") {
81-
// CORS Preflight
82-
reply.send();
83-
} else {
84-
const targetURL = request.query.url;
85-
86-
try {
87-
const response = await axios.get(targetURL, {
88-
responseType: "arraybuffer"
89-
});
90-
91-
if (!`${response.headers["content-type"]}`.includes("image")) {
92-
return reply.status(400).send("Invalid Content-Type");
93-
}
94-
95-
reply.header("content-type", response.headers["content-type"]);
96-
reply.send(Buffer.from(response.data, "binary"));
97-
} catch (error) {
98-
// eslint-disable-next-line no-console
99-
console.error(error);
100-
101-
return reply.status(500).send("Could not fetch");
102-
}
103-
}
104-
});
10553
fastify.post<{
10654
Body: Buffer;
10755
}>("/upload", async (req, res) => {

Diff for: apps/backend/app/src/index.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import { appService } from "./app";
22
import { createServer } from "@vrite/backend";
33

44
(async () => {
5-
const server = await createServer();
5+
const server = await createServer({
6+
database: true,
7+
pubSub: true,
8+
auth: true,
9+
email: true,
10+
gitSync: true,
11+
search: true,
12+
storage: true
13+
});
614

715
await server.register(appService);
816
server.listen({ host: server.config.HOST, port: server.config.PORT }, (err) => {

Diff for: apps/backend/assets/src/api.ts renamed to apps/backend/assets/src/assets.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ const assetsService = publicPlugin(async (fastify) => {
4646
await reply.header("Content-Type", sourceContentType).send(sourceAsset);
4747
};
4848

49-
reply.header("Access-Control-Allow-Origin", fastify.config.PUBLIC_APP_URL);
49+
reply.header(
50+
"Access-Control-Allow-Origin",
51+
fastify.config.NODE_ENV === "development" ? "*" : fastify.config.PUBLIC_APP_URL
52+
);
5053
reply.header("Access-Control-Allow-Methods", "GET");
5154

5255
if (!sourceAsset) return reply.status(404).send();

Diff for: apps/backend/assets/src/index.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { assetsService } from "./api";
1+
import { assetsService } from "./assets";
22
import { createServer } from "@vrite/backend";
33

44
(async () => {
5-
const server = await createServer();
5+
const server = await createServer({
6+
storage: true
7+
});
68

79
await server.register(assetsService);
810

Diff for: apps/backend/collaboration/src/extensions/git-sync.ts

+4-14
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import { Extension, onChangePayload, onDisconnectPayload } from "@hocuspocus/server";
22
import {
3-
GitData,
4-
ObjectId,
53
createGenericOutputContentProcessor,
64
docToJSON,
75
getContentPiecesCollection,
86
getGitDataCollection,
9-
jsonToBuffer
7+
jsonToBuffer,
8+
publishGitDataEvent
109
} from "@vrite/backend";
11-
import { createEventPublisher } from "@vrite/backend/src/lib/pub-sub";
1210
import { FastifyInstance } from "fastify";
11+
import { ObjectId } from "mongodb";
1312
import crypto from "node:crypto";
1413

1514
interface Configuration {
1615
debounce: number | false | null;
1716
debounceMaxWait: number;
1817
}
1918

20-
type GitDataEvent = {
21-
action: "update";
22-
data: Partial<GitData>;
23-
};
24-
2519
class GitSync implements Extension {
2620
private configuration: Configuration = {
2721
debounce: 5000,
@@ -36,10 +30,6 @@ class GitSync implements Extension {
3630

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

39-
private publishGitDataEvent = createEventPublisher<GitDataEvent>((workspaceId) => {
40-
return `gitData:${workspaceId}`;
41-
});
42-
4333
public constructor(fastify: FastifyInstance, configuration?: Partial<Configuration>) {
4434
this.fastify = fastify;
4535
this.configuration = {
@@ -121,7 +111,7 @@ class GitSync implements Extension {
121111
}
122112
}
123113
);
124-
this.publishGitDataEvent({ fastify: this.fastify }, `${details.context.workspaceId}`, {
114+
publishGitDataEvent({ fastify: this.fastify }, `${details.context.workspaceId}`, {
125115
action: "update",
126116
data: {
127117
records: gitData.records.map((record: any) => {

Diff for: apps/backend/collaboration/src/extensions/search-indexing.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Extension, onChangePayload, onDisconnectPayload } from "@hocuspocus/server";
2-
import { ObjectId, docToBuffer, getContentPiecesCollection } from "@vrite/backend";
2+
import { docToBuffer, getContentPiecesCollection } from "@vrite/backend";
33
import { FastifyInstance } from "fastify";
4+
import { ObjectId } from "mongodb";
45

56
interface Configuration {
67
debounce: number | false | null;

Diff for: apps/backend/collaboration/src/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { writingPlugin } from "./writing";
22
import { createServer } from "@vrite/backend";
33

44
(async () => {
5-
const server = await createServer();
5+
const server = await createServer({
6+
database: true,
7+
auth: true,
8+
pubSub: true,
9+
search: true
10+
});
611

712
await server.register(writingPlugin);
813
})();

Diff for: apps/backend/collaboration/src/writing.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import { publicPlugin, getContentsCollection, getContentVariantsCollection } from "@vrite/backend";
1+
import {
2+
publicPlugin,
3+
getContentsCollection,
4+
getContentVariantsCollection,
5+
errors,
6+
SessionData
7+
} from "@vrite/backend";
28
import { Server } from "@hocuspocus/server";
39
import { Database } from "@hocuspocus/extension-database";
410
import { Redis } from "@hocuspocus/extension-redis";
511
import { ObjectId, Binary } from "mongodb";
6-
import { SessionData } from "@vrite/backend/src/lib/session";
7-
import { unauthorized } from "@vrite/backend/src/lib/errors";
812
import { SearchIndexing } from "#extensions/search-indexing";
913
import { GitSync } from "#extensions/git-sync";
1014

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

2024
if (!cookies.accessToken) {
21-
throw unauthorized();
25+
throw errors.unauthorized();
2226
}
2327

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

2630
if (!token) {
27-
throw unauthorized();
31+
throw errors.unauthorized();
2832
}
2933

3034
const { sessionId } = fastify.jwt.verify<{ sessionId: string }>(token);

Diff for: apps/backend/extensions/package.json

+23-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,34 @@
1010
},
1111
"dependencies": {
1212
"@fastify/cors": "^8.3.0",
13+
"@prettier/sync": "^0.3.0",
1314
"@trpc/server": "^10.35.0",
15+
"@types/hast": "^3.0.1",
16+
"@types/html-to-text": "^9.0.1",
17+
"@types/js-yaml": "^4.0.6",
18+
"@types/mdast": "^4.0.1",
1419
"@vrite/backend": "workspace:*",
1520
"@vrite/sdk": "workspace:*",
21+
"dayjs": "^1.11.9",
1622
"fastify": "^4.20.0",
23+
"hast-util-to-html": "^9.0.0",
24+
"html-to-text": "^9.0.5",
25+
"js-yaml": "^4.1.0",
26+
"mdast-util-from-markdown": "^2.0.0",
27+
"mdast-util-frontmatter": "^2.0.1",
28+
"mdast-util-gfm": "^3.0.0",
29+
"mdast-util-mdx": "^3.0.0",
30+
"mdast-util-to-hast": "^13.0.2",
31+
"micromark-extension-frontmatter": "^2.0.0",
32+
"micromark-extension-gfm": "^3.0.0",
33+
"micromark-extension-mdxjs": "^2.0.0",
1734
"openai": "^4.0.0",
18-
"trpc-openapi": "^1.2.0"
35+
"prettier": "^3.0.2",
36+
"remark": "^15.0.1",
37+
"remark-mdx": "^2.3.0",
38+
"remark-parse": "^11.0.0",
39+
"trpc-openapi": "^1.2.0",
40+
"unist-util-visit": "^5.0.0"
1941
},
2042
"devDependencies": {
2143
"@vrite/scripts": "workspace:*"

0 commit comments

Comments
 (0)