-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserverUtils.js
More file actions
145 lines (107 loc) · 3.54 KB
/
Copy pathserverUtils.js
File metadata and controls
145 lines (107 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import fs from "fs";
import path from "path";
import archiver from "archiver";
import unzipper from "unzipper";
const ROOT = process.cwd();
export const SCORES_DIR = path.join(ROOT, "public/scores");
export const TEMPLATE_DIR = path.join(SCORES_DIR, "template");
export function assertSafeProjectName(name) {
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
throw new Error(
"Invalid project name. Use only letters, numbers, hyphens, and underscores."
);
}
}
function projectPath(name) {
return path.join(SCORES_DIR, name);
}
export function createNewProject(projectName) {
assertSafeProjectName(projectName);
const targetDir = projectPath(projectName);
if (fs.existsSync(targetDir)) {
throw new Error("Project already exists");
}
fs.cpSync(TEMPLATE_DIR, targetDir, {
recursive: true,
errorOnExist: true
});
// Optional: update preferences.json
const prefsPath = path.join(targetDir, "preferences.json");
if (fs.existsSync(prefsPath)) {
const prefs = JSON.parse(fs.readFileSync(prefsPath, "utf8"));
prefs.projectName = projectName;
fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2));
}
return projectName;
}
export function saveProjectAs(sourceProject, newProjectName) {
assertSafeProjectName(newProjectName);
const sourceDir = projectPath(sourceProject);
const targetDir = projectPath(newProjectName);
if (!fs.existsSync(sourceDir)) {
throw new Error("Source project not found");
}
if (fs.existsSync(targetDir)) {
throw new Error("Target project already exists");
}
fs.cpSync(sourceDir, targetDir, {
recursive: true,
errorOnExist: true
});
// Optional: update preferences.json
const prefsPath = path.join(targetDir, "preferences.json");
if (fs.existsSync(prefsPath)) {
const prefs = JSON.parse(fs.readFileSync(prefsPath, "utf8"));
prefs.projectName = newProjectName;
fs.writeFileSync(prefsPath, JSON.stringify(prefs, null, 2));
}
return newProjectName;
}
export function exportProject(projectName, res, oscillaVersion) {
const dir = projectPath(projectName);
if (!fs.existsSync(dir)) {
throw new Error("Project not found");
}
res.setHeader("Content-Type", "application/octet-stream");
res.setHeader(
"Content-Disposition",
`attachment; filename="${projectName}.oscilla"`
);
const archive = archiver("zip", { zlib: { level: 9 } });
archive.pipe(res);
// Manifest
archive.append(
JSON.stringify({
format: "oscilla-project",
oscillaVersion,
entry: "score.svg",
created: new Date().toISOString()
}, null, 2),
{ name: "oscilla.manifest.json" }
);
archive.directory(dir, false);
archive.finalize();
}
export async function importProject(buffer, projectName) {
assertSafeProjectName(projectName);
const targetDir = projectPath(projectName);
if (fs.existsSync(targetDir)) {
throw new Error("Project already exists");
}
fs.mkdirSync(targetDir, { recursive: true });
const directory = await unzipper.Open.buffer(buffer);
await directory.extract({ path: targetDir });
// Validate manifest
const manifestPath = path.join(targetDir, "oscilla.manifest.json");
if (!fs.existsSync(manifestPath)) {
fs.rmSync(targetDir, { recursive: true, force: true });
throw new Error("Invalid .oscilla file (no manifest)");
}
// Validate score
const scorePath = path.join(targetDir, "score.svg");
if (!fs.existsSync(scorePath)) {
fs.rmSync(targetDir, { recursive: true, force: true });
throw new Error("Invalid project (missing score.svg)");
}
return projectName;
}