Skip to content

Commit

Permalink
[WEB-1116] feat: pages realtime sync (#5057)
Browse files Browse the repository at this point in the history
* init: live server for editor realtime sync

* chore: authentication added

* chore: updated logic to convert html to binary for old pages

* chore: added description json on page update

* chore: made all functions generic

* chore: save description in json and html formats

* refactor: document editor components

* chore: uncomment ui package components

* fix: without props extensions refactor

* fix: merge conflicts resolved from preview

* chore: init docker compose

* chore: pages custom error codes

* chore: add health check endpoint to the live server

* chore: update without props extensions type

* chore: better error handling

* chore: update react-hook-form versions

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
  • Loading branch information
3 people committed Jul 26, 2024
1 parent 2c60967 commit f001bfa
Show file tree
Hide file tree
Showing 61 changed files with 3,177 additions and 3,477 deletions.
4 changes: 2 additions & 2 deletions admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"postcss": "^8.4.38",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.0",
"react-hook-form": "7.51.5",
"swr": "^2.2.4",
"tailwindcss": "3.3.2",
"uuid": "^9.0.1",
Expand All @@ -47,4 +47,4 @@
"tsconfig": "*",
"typescript": "^5.4.2"
}
}
}
9 changes: 6 additions & 3 deletions apiserver/plane/app/views/issue/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from plane.app.serializers import (
IssueFlatSerializer,
IssueSerializer,
IssueDetailSerializer
IssueDetailSerializer,
)
from plane.bgtasks.issue_activites_task import issue_activity
from plane.db.models import (
Expand All @@ -46,6 +46,7 @@
GroupedOffsetPaginator,
SubGroupedOffsetPaginator,
)
from plane.utils.error_codes import ERROR_CODES

# Module imports
from .. import BaseViewSet, BaseAPIView
Expand Down Expand Up @@ -341,8 +342,10 @@ def post(self, request, slug, project_id):
if issue.state.group not in ["completed", "cancelled"]:
return Response(
{
"error_code": 4091,
"error_message": "INVALID_ARCHIVE_STATE_GROUP"
"error_code": ERROR_CODES[
"INVALID_ARCHIVE_STATE_GROUP"
],
"error_message": "INVALID_ARCHIVE_STATE_GROUP",
},
status=status.HTTP_400_BAD_REQUEST,
)
Expand Down
23 changes: 17 additions & 6 deletions apiserver/plane/app/views/issue/bulk_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
IssueAssignee,
)
from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.error_codes import ERROR_CODES


class BulkIssueOperationsEndpoint(BaseAPIView):
Expand Down Expand Up @@ -59,14 +60,20 @@ def post(self, request, slug, project_id):

properties = request.data.get("properties", {})

if properties.get("start_date", False) and properties.get("target_date", False):
if properties.get("start_date", False) and properties.get(
"target_date", False
):
if (
datetime.strptime(properties.get("start_date"), "%Y-%m-%d").date()
> datetime.strptime(properties.get("target_date"), "%Y-%m-%d").date()
datetime.strptime(
properties.get("start_date"), "%Y-%m-%d"
).date()
> datetime.strptime(
properties.get("target_date"), "%Y-%m-%d"
).date()
):
return Response(
{
"error_code": 4100,
"error_code": ERROR_CODES["INVALID_ISSUE_DATES"],
"error_message": "INVALID_ISSUE_DATES",
},
status=status.HTTP_400_BAD_REQUEST,
Expand Down Expand Up @@ -124,7 +131,9 @@ def post(self, request, slug, project_id):
):
return Response(
{
"error_code": 4101,
"error_code": ERROR_CODES[
"INVALID_ISSUE_START_DATE"
],
"error_message": "INVALID_ISSUE_START_DATE",
},
status=status.HTTP_400_BAD_REQUEST,
Expand Down Expand Up @@ -158,7 +167,9 @@ def post(self, request, slug, project_id):
):
return Response(
{
"error_code": 4102,
"error_code": ERROR_CODES[
"INVALID_ISSUE_TARGET_DATE"
],
"error_message": "INVALID_ISSUE_TARGET_DATE",
},
status=status.HTTP_400_BAD_REQUEST,
Expand Down
17 changes: 12 additions & 5 deletions apiserver/plane/app/views/page/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
ProjectMember,
ProjectPage,
)

from plane.utils.error_codes import ERROR_CODES
# Module imports
from ..base import BaseAPIView, BaseViewSet

Expand Down Expand Up @@ -514,14 +514,20 @@ def partial_update(self, request, slug, project_id, pk):

if page.is_locked:
return Response(
{"error": "Page is locked"},
status=471,
{
"error_code": ERROR_CODES["PAGE_LOCKED"],
"error_message": "PAGE_LOCKED",
},
status=status.HTTP_400_BAD_REQUEST,
)

if page.archived_at:
return Response(
{"error": "Page is archived"},
status=472,
{
"error_code": ERROR_CODES["PAGE_ARCHIVED"],
"error_message": "PAGE_ARCHIVED",
},
status=status.HTTP_400_BAD_REQUEST,
)

# Serialize the existing instance
Expand Down Expand Up @@ -549,6 +555,7 @@ def partial_update(self, request, slug, project_id, pk):
# Store the updated binary data
page.description_binary = new_binary_data
page.description_html = request.data.get("description_html")
page.description = request.data.get("description")
page.save()
# Return a success response
page_version.delay(
Expand Down
10 changes: 10 additions & 0 deletions apiserver/plane/utils/error_codes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ERROR_CODES = {
# issues
"INVALID_ARCHIVE_STATE_GROUP": 4091,
"INVALID_ISSUE_DATES": 4100,
"INVALID_ISSUE_START_DATE": 4101,
"INVALID_ISSUE_TARGET_DATE": 4102,
# pages
"PAGE_LOCKED": 4701,
"PAGE_ARCHIVED": 4702,
}
14 changes: 14 additions & 0 deletions docker-compose-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ services:
- worker
- web

live:
build:
context: .
dockerfile: ./live/Dockerfile.dev
restart: unless-stopped
networks:
- dev_env
volumes:
- ./live:/app/live
depends_on:
- api
- worker
- web

api:
build:
context: ./apiserver
Expand Down
1 change: 1 addition & 0 deletions live/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
API_BASE_URL="http://api:8000"
28 changes: 28 additions & 0 deletions live/Dockerfile.channel
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Use a Node.js base image
FROM node:latest

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY ./channel/package*.json ./

# Install dependencies
RUN yarn install

# COPY ./plane/editor-core ./node_modules/@plane/editor-core

# Install TypeScript
RUN yarn add typescript

# Copy the rest of the application
COPY ./channel .

# Compile TypeScript to JavaScript
RUN npx tsc

# Expose port 3003
EXPOSE 3003

# Start the Node.js server
CMD ["node", "dist/index.js"]
13 changes: 13 additions & 0 deletions live/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:18-alpine
RUN apk add --no-cache libc6-compat
# Set working directory
WORKDIR /app


COPY . .
RUN yarn global add turbo
RUN yarn install
EXPOSE 3003

VOLUME [ "/app/node_modules", "/app/channel/node_modules"]
CMD ["yarn","dev", "--filter=live"]
42 changes: 42 additions & 0 deletions live/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "live",
"version": "0.22.0",
"description": "",
"main": "./src/index.ts",
"scripts": {
"build": "tsup",
"start": "node dist/index.js",
"dev": "tsup --watch"
},
"keywords": [],
"type": "module",
"author": "",
"license": "ISC",
"dependencies": {
"@hocuspocus/extension-database": "^2.11.3",
"@hocuspocus/extension-logger": "^2.11.3",
"@hocuspocus/server": "^2.11.3",
"@plane/editor": "*",
"@plane/types": "*",
"@tiptap/core": "^2.4.0",
"@tiptap/html": "^2.3.0",
"axios": "^1.7.2",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-ws": "^5.0.2",
"lodash": "^4.17.21",
"y-prosemirror": "^1.2.9",
"y-protocols": "^1.0.6",
"yjs": "^13.6.14"
},
"devDependencies": {
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.21",
"@types/express-ws": "^3.0.4",
"@types/node": "^20.14.9",
"tsup": "^7.2.0",
"nodemon": "^3.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
}
}
61 changes: 61 additions & 0 deletions live/src/authentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ConnectionConfiguration } from "@hocuspocus/server";
// services
import { UserService } from "./services/user.service.js";
// types
import { TDocumentTypes } from "./types/common.js";
const userService = new UserService();

type Props = {
connection: ConnectionConfiguration;
cookie: string;
params: URLSearchParams;
token: string;
};

export const handleAuthentication = async (props: Props) => {
const { connection, cookie, params, token } = props;
// params
const workspaceSlug = params.get("workspaceSlug")?.toString();
const projectId = params.get("projectId")?.toString();
const documentType = params.get("documentType")?.toString() as
| TDocumentTypes
| undefined;
// fetch current user info
let response;
try {
response = await userService.currentUser(cookie);
} catch (error) {
console.error("Failed to fetch current user:", error);
throw error;
}
if (response.id !== token) {
throw Error("Authentication failed: Token doesn't match the current user.");
}

if (documentType === "project_page") {
if (!workspaceSlug || !projectId) {
throw Error(
"Authentication failed: Incomplete query params. Either workspaceSlug or projectId is missing.",
);
}
// fetch current user's roles
const workspaceRoles = await userService.getUserAllProjectsRole(
workspaceSlug,
cookie,
);
const currentProjectRole = workspaceRoles[projectId];
// make the connection read only for roles lower than a member
if (currentProjectRole < 15) {
connection.readOnly = true;
}
} else {
throw Error("Authentication failed: Invalid document type provided.");
}

return {
user: {
id: response.id,
name: response.display_name,
},
};
};
Loading

0 comments on commit f001bfa

Please sign in to comment.