kanbanqube 1.0.1 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +81 -0
- package/config/index.js +54 -0
- package/config/mimeTypes.js +18 -0
- package/controllers/boardController.js +20 -0
- package/controllers/configController.js +23 -0
- package/controllers/importController.js +22 -0
- package/controllers/syncController.js +35 -0
- package/controllers/uploadController.js +21 -0
- package/middleware/asyncHandler.js +11 -0
- package/middleware/errorHandler.js +12 -0
- package/middleware/notFound.js +9 -0
- package/models/boardRepository.js +99 -0
- package/models/syncStatusStore.js +23 -0
- package/package.json +15 -3
- package/routes/apiRoutes.js +33 -0
- package/server.js +13 -1322
- package/services/boardNormalizer.js +417 -0
- package/services/boardService.js +67 -0
- package/services/gitService.js +174 -0
- package/services/gitSyncService.js +102 -0
- package/services/importService.js +29 -0
- package/services/uploadService.js +110 -0
- package/utils/dateUtils.js +22 -0
- package/utils/fileNameUtils.js +43 -0
- package/utils/fileUtils.js +48 -0
- package/utils/idUtils.js +11 -0
- package/utils/multipart.js +45 -0
- package/utils/positionUtils.js +10 -0
- package/utils/requestBody.js +18 -0
- package/utils/stringUtils.js +42 -0
package/app.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const express = require("express");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const mimeTypes = require("./config/mimeTypes");
|
|
6
|
+
const { errorHandler } = require("./middleware/errorHandler");
|
|
7
|
+
const { notFound } = require("./middleware/notFound");
|
|
8
|
+
const { createApiRoutes } = require("./routes/apiRoutes");
|
|
9
|
+
const { createBoardService } = require("./services/boardService");
|
|
10
|
+
const { createGitSyncService } = require("./services/gitSyncService");
|
|
11
|
+
const { createImportService } = require("./services/importService");
|
|
12
|
+
const { createUploadService } = require("./services/uploadService");
|
|
13
|
+
|
|
14
|
+
function createApp(config) {
|
|
15
|
+
const app = express();
|
|
16
|
+
const boardService = createBoardService(config);
|
|
17
|
+
const gitSyncService = createGitSyncService(config);
|
|
18
|
+
const importService = createImportService(config);
|
|
19
|
+
const uploadService = createUploadService(config, boardService);
|
|
20
|
+
|
|
21
|
+
app.disable("x-powered-by");
|
|
22
|
+
app.use(express.json({ limit: "5mb", type: "application/json" }));
|
|
23
|
+
app.use(noStore);
|
|
24
|
+
|
|
25
|
+
app.use("/api", createApiRoutes({
|
|
26
|
+
config,
|
|
27
|
+
boardService,
|
|
28
|
+
gitSyncService,
|
|
29
|
+
importService,
|
|
30
|
+
uploadService
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
app.get(`/${config.demoBoardFileName}`, (_request, response) => {
|
|
34
|
+
response.type("json").sendFile(config.demoBoardFilePath);
|
|
35
|
+
});
|
|
36
|
+
app.get(`/${config.uploadsDirName}/:fileName`, (request, response, next) => {
|
|
37
|
+
let fileName = "";
|
|
38
|
+
try {
|
|
39
|
+
fileName = uploadService.safeUploadFileName(request.params.fileName);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
error.statusCode = 400;
|
|
42
|
+
next(error);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const filePath = path.join(config.uploadsDir, fileName);
|
|
46
|
+
response.type(mimeTypes[path.extname(filePath).toLowerCase()] || "application/octet-stream");
|
|
47
|
+
response.sendFile(filePath, (error) => {
|
|
48
|
+
if (error) next(error);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
app.use(express.static(config.publicDir, {
|
|
52
|
+
etag: false,
|
|
53
|
+
lastModified: false,
|
|
54
|
+
setHeaders: noStoreHeaders
|
|
55
|
+
}));
|
|
56
|
+
app.use(notFound);
|
|
57
|
+
app.use(errorHandler);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
app,
|
|
61
|
+
services: {
|
|
62
|
+
boardService,
|
|
63
|
+
gitSyncService,
|
|
64
|
+
importService,
|
|
65
|
+
uploadService
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function noStore(_request, response, next) {
|
|
71
|
+
noStoreHeaders(response);
|
|
72
|
+
next();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function noStoreHeaders(response) {
|
|
76
|
+
response.setHeader("Cache-Control", "no-store");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
createApp
|
|
81
|
+
};
|
package/config/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
function resolveWorkspaceDirectory(argument) {
|
|
6
|
+
if (typeof argument === "string" && argument.trim()) {
|
|
7
|
+
return path.resolve(argument);
|
|
8
|
+
}
|
|
9
|
+
return process.cwd();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createConfig(options = {}) {
|
|
13
|
+
const appDir = options.appDir || path.resolve(__dirname, "..");
|
|
14
|
+
const workspaceDir = resolveWorkspaceDirectory(options.workspaceArgument);
|
|
15
|
+
const boardFileName = "board.json";
|
|
16
|
+
const demoBoardFileName = "demo_board.json";
|
|
17
|
+
const boardDirName = "board";
|
|
18
|
+
const uploadsDirName = "uploads";
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
appDir,
|
|
22
|
+
workspaceDir,
|
|
23
|
+
publicDir: path.join(appDir, "public"),
|
|
24
|
+
boardFileName,
|
|
25
|
+
boardFilePath: path.join(workspaceDir, boardFileName),
|
|
26
|
+
demoBoardFileName,
|
|
27
|
+
demoBoardFilePath: path.join(appDir, demoBoardFileName),
|
|
28
|
+
boardDirName,
|
|
29
|
+
boardDir: path.join(workspaceDir, boardDirName),
|
|
30
|
+
boardMetaFilePath: path.join(workspaceDir, boardDirName, "meta.json"),
|
|
31
|
+
uploadsDirName,
|
|
32
|
+
uploadsDir: path.join(workspaceDir, uploadsDirName),
|
|
33
|
+
sampleExportDir: path.join(workspaceDir, "trello_export"),
|
|
34
|
+
port: Number(options.port || process.env.PORT || 3000),
|
|
35
|
+
gitExecutableCandidates: [
|
|
36
|
+
"/usr/bin/git",
|
|
37
|
+
"/bin/git",
|
|
38
|
+
"/usr/local/bin/git",
|
|
39
|
+
"/opt/homebrew/bin/git"
|
|
40
|
+
],
|
|
41
|
+
sshExecutableCandidates: [
|
|
42
|
+
"/usr/bin/ssh",
|
|
43
|
+
"/bin/ssh",
|
|
44
|
+
"/usr/local/bin/ssh",
|
|
45
|
+
"/opt/homebrew/bin/ssh"
|
|
46
|
+
],
|
|
47
|
+
gitSafePath: "/usr/bin:/bin:/usr/local/bin:/opt/homebrew/bin"
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
createConfig,
|
|
53
|
+
resolveWorkspaceDirectory
|
|
54
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const mimeTypes = {
|
|
4
|
+
".css": "text/css; charset=utf-8",
|
|
5
|
+
".html": "text/html; charset=utf-8",
|
|
6
|
+
".js": "application/javascript; charset=utf-8",
|
|
7
|
+
".json": "application/json; charset=utf-8",
|
|
8
|
+
".svg": "image/svg+xml",
|
|
9
|
+
".png": "image/png",
|
|
10
|
+
".jpg": "image/jpeg",
|
|
11
|
+
".jpeg": "image/jpeg",
|
|
12
|
+
".gif": "image/gif",
|
|
13
|
+
".webp": "image/webp",
|
|
14
|
+
".pdf": "application/pdf",
|
|
15
|
+
".txt": "text/plain; charset=utf-8"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = mimeTypes;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function createBoardController(boardService) {
|
|
4
|
+
async function getBoard(_request, response) {
|
|
5
|
+
response.json(await boardService.loadBoard());
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function saveBoard(request, response) {
|
|
9
|
+
response.json(await boardService.saveBoard(request.body || {}));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
getBoard,
|
|
14
|
+
saveBoard
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
createBoardController
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function createConfigController(config, gitService) {
|
|
4
|
+
async function getConfig(_request, response) {
|
|
5
|
+
response.json({
|
|
6
|
+
boardFile: config.boardFileName,
|
|
7
|
+
storagePath: config.boardDirName,
|
|
8
|
+
workspacePath: config.workspaceDir,
|
|
9
|
+
hasGitRepo: await gitService.hasGitRepository(config.workspaceDir),
|
|
10
|
+
gitRemote: await gitService.gitRemoteOrigin(config.workspaceDir),
|
|
11
|
+
gitUserName: await gitService.gitUserName(config.workspaceDir),
|
|
12
|
+
gitUserEmail: await gitService.gitUserEmail(config.workspaceDir)
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
getConfig
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
createConfigController
|
|
23
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function createImportController(boardService, importService) {
|
|
4
|
+
async function importBoard(request, response) {
|
|
5
|
+
const currentBoard = await boardService.loadBoard();
|
|
6
|
+
if ((currentBoard.cards || []).length > 0) {
|
|
7
|
+
response.status(409).json({ error: "Import is only available when the board has no cards." });
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const importedBoard = await importService.readImportedBoard(request);
|
|
12
|
+
response.json(await boardService.saveBoard(importedBoard));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
importBoard
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
createImportController
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { getSyncStatus } = require("../models/syncStatusStore");
|
|
4
|
+
|
|
5
|
+
function createSyncController(gitSyncService) {
|
|
6
|
+
return {
|
|
7
|
+
syncStatus,
|
|
8
|
+
syncBoard: (request, response) => syncBoard(request, response, gitSyncService)
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function syncStatus(_request, response) {
|
|
13
|
+
response.json(getSyncStatus());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function syncBoard(_request, response, gitSyncService) {
|
|
17
|
+
const status = getSyncStatus();
|
|
18
|
+
if (status.running) {
|
|
19
|
+
response.status(409).json({
|
|
20
|
+
ok: false,
|
|
21
|
+
output: status.output || "A git sync is already in progress.",
|
|
22
|
+
startedAt: status.startedAt,
|
|
23
|
+
finishedAt: status.finishedAt
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = await gitSyncService.syncBoardRepository();
|
|
29
|
+
response.status(result.ok ? 200 : 500).json(result);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
createSyncController,
|
|
34
|
+
syncStatus
|
|
35
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function createUploadController(uploadService) {
|
|
4
|
+
async function uploadFiles(request, response) {
|
|
5
|
+
const files = await uploadService.saveUploadedFiles(request);
|
|
6
|
+
response.json({ files });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async function deleteUpload(request, response) {
|
|
10
|
+
response.json(await uploadService.deleteUploadedFile(request.params.fileName));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
uploadFiles,
|
|
15
|
+
deleteUpload
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
createUploadController
|
|
21
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function errorHandler(error, _request, response, _next) {
|
|
4
|
+
if (response.headersSent) return;
|
|
5
|
+
response.status(error.statusCode || 500).json({
|
|
6
|
+
error: error.message || "Unexpected server error."
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
errorHandler
|
|
12
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs/promises");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const { createHexId } = require("../utils/idUtils");
|
|
6
|
+
const { readJsonFile, writeJsonIfChanged } = require("../utils/fileUtils");
|
|
7
|
+
const { nonEmptyString } = require("../utils/stringUtils");
|
|
8
|
+
|
|
9
|
+
function createBoardRepository(config) {
|
|
10
|
+
async function readSplitBoard() {
|
|
11
|
+
const meta = await readJsonFile(config.boardMetaFilePath, {});
|
|
12
|
+
return {
|
|
13
|
+
...meta,
|
|
14
|
+
lists: await readJsonCollection("lists"),
|
|
15
|
+
labels: await readJsonCollection("labels"),
|
|
16
|
+
members: await readJsonCollection("members"),
|
|
17
|
+
cards: await readJsonCollection("cards"),
|
|
18
|
+
checklists: await readJsonCollection("checklists"),
|
|
19
|
+
actions: await readJsonCollection("actions")
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function writeSplitBoard(board) {
|
|
24
|
+
await fs.mkdir(config.boardDir, { recursive: true });
|
|
25
|
+
const {
|
|
26
|
+
lists,
|
|
27
|
+
labels,
|
|
28
|
+
members,
|
|
29
|
+
cards,
|
|
30
|
+
checklists,
|
|
31
|
+
actions,
|
|
32
|
+
...meta
|
|
33
|
+
} = board;
|
|
34
|
+
|
|
35
|
+
await writeJsonIfChanged(config.boardMetaFilePath, meta);
|
|
36
|
+
await writeJsonCollection("lists", lists || []);
|
|
37
|
+
await writeJsonCollection("labels", labels || []);
|
|
38
|
+
await writeJsonCollection("members", members || []);
|
|
39
|
+
await writeJsonCollection("cards", cards || []);
|
|
40
|
+
await writeJsonCollection("checklists", checklists || []);
|
|
41
|
+
await writeJsonCollection("actions", actions || []);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function readJsonCollection(name) {
|
|
45
|
+
const directory = path.join(config.boardDir, name);
|
|
46
|
+
let entries = [];
|
|
47
|
+
try {
|
|
48
|
+
entries = await fs.readdir(directory, { withFileTypes: true });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error.code === "ENOENT") return [];
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const items = [];
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
57
|
+
items.push(await readJsonFile(path.join(directory, entry.name), null));
|
|
58
|
+
}
|
|
59
|
+
return items.filter(Boolean);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function writeJsonCollection(name, items) {
|
|
63
|
+
const directory = path.join(config.boardDir, name);
|
|
64
|
+
await fs.mkdir(directory, { recursive: true });
|
|
65
|
+
const desiredFiles = new Set();
|
|
66
|
+
|
|
67
|
+
for (const item of items) {
|
|
68
|
+
if (!item || typeof item !== "object") continue;
|
|
69
|
+
const id = nonEmptyString(item.id) || createHexId();
|
|
70
|
+
item.id = id;
|
|
71
|
+
const fileName = `${encodeURIComponent(id)}.json`;
|
|
72
|
+
desiredFiles.add(fileName);
|
|
73
|
+
await writeJsonIfChanged(path.join(directory, fileName), item);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let entries = [];
|
|
77
|
+
try {
|
|
78
|
+
entries = await fs.readdir(directory, { withFileTypes: true });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error.code === "ENOENT") return;
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (entry.isFile() && entry.name.endsWith(".json") && !desiredFiles.has(entry.name)) {
|
|
86
|
+
await fs.unlink(path.join(directory, entry.name));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
readSplitBoard,
|
|
93
|
+
writeSplitBoard
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = {
|
|
98
|
+
createBoardRepository
|
|
99
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const syncStatus = {
|
|
4
|
+
running: false,
|
|
5
|
+
startedAt: "",
|
|
6
|
+
finishedAt: "",
|
|
7
|
+
ok: null,
|
|
8
|
+
output: ""
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function getSyncStatus() {
|
|
12
|
+
return { ...syncStatus };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function updateSyncStatus(values) {
|
|
16
|
+
Object.assign(syncStatus, values);
|
|
17
|
+
return getSyncStatus();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
getSyncStatus,
|
|
22
|
+
updateSyncStatus
|
|
23
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kanbanqube",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Local-first Kanban board backed by normal files",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Mathias Conradt",
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"server.js",
|
|
28
|
+
"app.js",
|
|
29
|
+
"config/",
|
|
30
|
+
"controllers/",
|
|
31
|
+
"middleware/",
|
|
32
|
+
"models/",
|
|
33
|
+
"routes/",
|
|
34
|
+
"services/",
|
|
35
|
+
"utils/",
|
|
28
36
|
"public/",
|
|
29
37
|
"demo_board.json",
|
|
30
38
|
"promo.jpg",
|
|
@@ -33,14 +41,18 @@
|
|
|
33
41
|
"LICENSE"
|
|
34
42
|
],
|
|
35
43
|
"bin": {
|
|
36
|
-
"kanbanqube": "
|
|
44
|
+
"kanbanqube": "server.js"
|
|
37
45
|
},
|
|
38
46
|
"scripts": {
|
|
39
47
|
"start": "node server.js",
|
|
40
|
-
"test": "node --check server.js && node --check --input-type=module < public/app.js",
|
|
48
|
+
"test": "npm run check:registry && node --check server.js && node --check app.js && find config controllers middleware models routes services utils -name '*.js' -exec node --check {} \\; && node --check --input-type=module < public/app.js",
|
|
49
|
+
"check:registry": "node -e \"const fs=require('fs'); const lock=fs.readFileSync('package-lock.json','utf8'); if(/repox|jfrog/i.test(lock)){ console.error('package-lock.json must use public npm registry URLs, not local Repox/JFrog URLs.'); process.exit(1); }\"",
|
|
41
50
|
"prepublishOnly": "npm test"
|
|
42
51
|
},
|
|
43
52
|
"publishConfig": {
|
|
44
53
|
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"express": "^5.2.1"
|
|
45
57
|
}
|
|
46
58
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const express = require("express");
|
|
4
|
+
const { createBoardController } = require("../controllers/boardController");
|
|
5
|
+
const { createConfigController } = require("../controllers/configController");
|
|
6
|
+
const { createImportController } = require("../controllers/importController");
|
|
7
|
+
const { createSyncController } = require("../controllers/syncController");
|
|
8
|
+
const { createUploadController } = require("../controllers/uploadController");
|
|
9
|
+
const { asyncHandler } = require("../middleware/asyncHandler");
|
|
10
|
+
|
|
11
|
+
function createApiRoutes(services) {
|
|
12
|
+
const router = express.Router();
|
|
13
|
+
const boardController = createBoardController(services.boardService);
|
|
14
|
+
const configController = createConfigController(services.config, services.gitSyncService.gitService);
|
|
15
|
+
const importController = createImportController(services.boardService, services.importService);
|
|
16
|
+
const syncController = createSyncController(services.gitSyncService);
|
|
17
|
+
const uploadController = createUploadController(services.uploadService);
|
|
18
|
+
|
|
19
|
+
router.get("/board", asyncHandler(boardController.getBoard));
|
|
20
|
+
router.put("/board", asyncHandler(boardController.saveBoard));
|
|
21
|
+
router.get("/config", asyncHandler(configController.getConfig));
|
|
22
|
+
router.post("/uploads", asyncHandler(uploadController.uploadFiles));
|
|
23
|
+
router.delete("/uploads/:fileName", asyncHandler(uploadController.deleteUpload));
|
|
24
|
+
router.post("/import", asyncHandler(importController.importBoard));
|
|
25
|
+
router.post("/sync", asyncHandler(syncController.syncBoard));
|
|
26
|
+
router.get("/sync-status", syncController.syncStatus);
|
|
27
|
+
|
|
28
|
+
return router;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
createApiRoutes
|
|
33
|
+
};
|