refactor: remove doc URL parameter, single document per Space
Browse filesAlways use "default" as the document name. One Space = one article.
Made-with: Cursor
- README.md +1 -1
- backend/src/server.ts +5 -15
- frontend/src/App.tsx +1 -4
README.md
CHANGED
|
@@ -32,4 +32,4 @@ cd frontend && npm install && npm run dev
|
|
| 32 |
|
| 33 |
Open http://localhost:5173. Open a second tab to see collaboration in action.
|
| 34 |
|
| 35 |
-
|
|
|
|
| 32 |
|
| 33 |
Open http://localhost:5173. Open a second tab to see collaboration in action.
|
| 34 |
|
| 35 |
+
The app uses a single shared document (`default`).
|
backend/src/server.ts
CHANGED
|
@@ -24,6 +24,9 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
| 24 |
const DATA_DIR = join(__dirname, "..", "data");
|
| 25 |
const PORT = parseInt(process.env.PORT || "8080", 10);
|
| 26 |
|
|
|
|
|
|
|
|
|
|
| 27 |
mkdirSync(DATA_DIR, { recursive: true });
|
| 28 |
|
| 29 |
function docPath(name: string) {
|
|
@@ -157,14 +160,8 @@ app.post("/api/publish", async (req, res) => {
|
|
| 157 |
}
|
| 158 |
}
|
| 159 |
|
| 160 |
-
const { docName } = req.body as { docName?: string };
|
| 161 |
-
if (!docName) {
|
| 162 |
-
res.status(400).json({ error: "docName is required" });
|
| 163 |
-
return;
|
| 164 |
-
}
|
| 165 |
-
|
| 166 |
try {
|
| 167 |
-
const result = await publishDocument(
|
| 168 |
if (!result.success) {
|
| 169 |
res.status(500).json({ error: result.error || "Publish failed" });
|
| 170 |
return;
|
|
@@ -260,11 +257,6 @@ const staticDir =
|
|
| 260 |
? join(__dirname, "..", "frontend-dist")
|
| 261 |
: join(__dirname, "..", "..", "frontend", "dist");
|
| 262 |
|
| 263 |
-
function getDocName(req: express.Request): string {
|
| 264 |
-
const raw = (req.query.doc as string) || "default";
|
| 265 |
-
return raw.replace(/[^a-zA-Z0-9_-]/g, "_");
|
| 266 |
-
}
|
| 267 |
-
|
| 268 |
function getPublishedPath(docName: string): string {
|
| 269 |
return join(DATA_DIR, "published", docName, "index.html");
|
| 270 |
}
|
|
@@ -310,7 +302,6 @@ if (existsSync(staticDir)) {
|
|
| 310 |
|
| 311 |
if (!user || !user.canEdit) {
|
| 312 |
// Not logged in or no write access → redirect to OAuth login
|
| 313 |
-
const docParam = req.query.doc ? `?doc=${req.query.doc}` : "";
|
| 314 |
res.redirect(`/oauth/authorize`);
|
| 315 |
return;
|
| 316 |
}
|
|
@@ -328,8 +319,7 @@ if (existsSync(staticDir)) {
|
|
| 328 |
}
|
| 329 |
|
| 330 |
// In production with OAuth: serve the published article if it exists
|
| 331 |
-
const
|
| 332 |
-
const publishedPath = getPublishedPath(docName);
|
| 333 |
|
| 334 |
if (existsSync(publishedPath)) {
|
| 335 |
res.sendFile(publishedPath);
|
|
|
|
| 24 |
const DATA_DIR = join(__dirname, "..", "data");
|
| 25 |
const PORT = parseInt(process.env.PORT || "8080", 10);
|
| 26 |
|
| 27 |
+
/** Single collab document; URL `?doc=` is not supported. */
|
| 28 |
+
const DEFAULT_DOC_NAME = "default";
|
| 29 |
+
|
| 30 |
mkdirSync(DATA_DIR, { recursive: true });
|
| 31 |
|
| 32 |
function docPath(name: string) {
|
|
|
|
| 160 |
}
|
| 161 |
}
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
try {
|
| 164 |
+
const result = await publishDocument(DEFAULT_DOC_NAME);
|
| 165 |
if (!result.success) {
|
| 166 |
res.status(500).json({ error: result.error || "Publish failed" });
|
| 167 |
return;
|
|
|
|
| 257 |
? join(__dirname, "..", "frontend-dist")
|
| 258 |
: join(__dirname, "..", "..", "frontend", "dist");
|
| 259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
function getPublishedPath(docName: string): string {
|
| 261 |
return join(DATA_DIR, "published", docName, "index.html");
|
| 262 |
}
|
|
|
|
| 302 |
|
| 303 |
if (!user || !user.canEdit) {
|
| 304 |
// Not logged in or no write access → redirect to OAuth login
|
|
|
|
| 305 |
res.redirect(`/oauth/authorize`);
|
| 306 |
return;
|
| 307 |
}
|
|
|
|
| 319 |
}
|
| 320 |
|
| 321 |
// In production with OAuth: serve the published article if it exists
|
| 322 |
+
const publishedPath = getPublishedPath(DEFAULT_DOC_NAME);
|
|
|
|
| 323 |
|
| 324 |
if (existsSync(publishedPath)) {
|
| 325 |
res.sendFile(publishedPath);
|
frontend/src/App.tsx
CHANGED
|
@@ -55,10 +55,7 @@ export default function App() {
|
|
| 55 |
color: randomColor(),
|
| 56 |
}));
|
| 57 |
|
| 58 |
-
const
|
| 59 |
-
const params = new URLSearchParams(window.location.search);
|
| 60 |
-
return params.get("doc") || "default";
|
| 61 |
-
});
|
| 62 |
|
| 63 |
const editorRef = useRef<TiptapEditor | null>(null);
|
| 64 |
const editorContainerRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
| 55 |
color: randomColor(),
|
| 56 |
}));
|
| 57 |
|
| 58 |
+
const docName = "default";
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
const editorRef = useRef<TiptapEditor | null>(null);
|
| 61 |
const editorContainerRef = useRef<HTMLDivElement | null>(null);
|