From 00b82c65489f3d038808ca373e6135139e79fa33 Mon Sep 17 00:00:00 2001 From: Prozilla Date: Mon, 10 Jun 2024 19:13:49 +0200 Subject: [PATCH] Added repo tree to default virtual drive data --- .gitignore | 3 +- deploy.sh | 2 +- docs/README.md | 6 + docs/features/virtual-drive/README.md | 2 +- package.json | 3 +- .../virtual-drive/root/defaultData.ts | 146 ++++++------------ src/tools/fetchRepository.ts | 65 ++++++++ 7 files changed, 123 insertions(+), 104 deletions(-) create mode 100644 src/tools/fetchRepository.ts diff --git a/.gitignore b/.gitignore index 4ba7535..946913d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ yarn-debug.log* yarn-error.log* *.ini -/tmp \ No newline at end of file +/tmp +/public/config/tree.json \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index 9ee27f3..bf0af26 100644 --- a/deploy.sh +++ b/deploy.sh @@ -15,7 +15,7 @@ echo -e "Commit message: \e[0;36m$COMMIT_MESSAGE\e[0m" echo -e "Repository: \e[0;36m$REPO_URL\e[0m" echo "" -if gh-pages -x -d dist -m $COMMIT_MESSAGE -r $REPO_URL ; then +if gh-pages -x -d dist -m "$COMMIT_MESSAGE" -r "$REPO_URL" ; then echo -e "\e[0;32m✓ Successfully deployed to \e[0;36mhttps://$domain/\e[0m" else echo -e "\e[0;31mFailed to deploy\e[0m" diff --git a/docs/README.md b/docs/README.md index 4245651..213e5a4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -49,6 +49,12 @@ These are the scripts in logical order, that will be available when you have ins > [!NOTE] > After this deployment process, GitHub Pages will run its own build step to finalize the deployment, as seen in the `Actions` tab on GitHub. This usually takes less than a minute and once it's done, your deployment will be live. +### Other scripts + +- `npm run fetch` + + Fetch the repository tree using GitHub's API and store it as a JSON file that will be used to populate the virtual drive. More information can be found on the [virtual drive](./features/virtual-drive/README.md) page. + ### Configuration See [docs/configuration](configuration/README.md) for more information. diff --git a/docs/features/virtual-drive/README.md b/docs/features/virtual-drive/README.md index 7d0fda4..1bf8655 100644 --- a/docs/features/virtual-drive/README.md +++ b/docs/features/virtual-drive/README.md @@ -27,7 +27,7 @@ export function Example() { The virtual root is a virtual folder that contains all other folders and files. It serves as the root directory for the virtual drive and is used as an access point in applications. -The default data for the virtual root is set in the `loadDefaultData()` function in the `VirtualRoot` class. +The default data for the virtual root is set in the `loadDefaultData()` function in the `VirtualRoot` class. It uses the contents of the GitHub repository to populate the root folder, allowing the user to browse the source code of ProzillaOS using its file explorer. To create or update the fetched repository tree data, so it can be used by ProzillaOS, run `npm run fetch`. This will fetch and cache the paths of every file and folder inside the repository. You should only run this command whenever a file/folder has been added/removed from the repo, because there is a [rate limit of 60 requests per hour](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api). For more information about how the virtual root is stored and loaded, check the [Storage](../storage/README.md) docs. diff --git a/package.json b/package.json index ae68dbb..79b9841 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "serve": "vite preview --port 8080 --host", "predeploy": "npm run build && npm run stage", "stage": "node --no-warnings --loader ts-node/esm ./src/tools/stage", - "deploy": "sh deploy.sh" + "deploy": "sh deploy.sh", + "fetch": "node --no-warnings --loader ts-node/esm ./src/tools/fetchRepository" }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.4.0", diff --git a/src/features/virtual-drive/root/defaultData.ts b/src/features/virtual-drive/root/defaultData.ts index ce6b804..6c23519 100644 --- a/src/features/virtual-drive/root/defaultData.ts +++ b/src/features/virtual-drive/root/defaultData.ts @@ -1,8 +1,8 @@ import { APPS } from "../../../config/apps.config"; import { WALLPAPERS } from "../../../config/desktop.config"; import AppsManager from "../../apps/appsManager"; -import { VirtualFileLink } from "../file"; -import { VirtualFolderLink } from "../folder"; +import { VirtualFile, VirtualFileLink } from "../file"; +import { VirtualFolder, VirtualFolderLink } from "../folder"; import { VirtualRoot } from "./virtualRoot"; /** @@ -22,7 +22,7 @@ export function loadDefaultData(virtualRoot: VirtualRoot) { }).createFile("apps", "xml", (file) => { file.setSource("/config/apps.xml"); }).createFile("theme", "xml", (file) => { - file.setSource("/config/apps.xml"); + file.setSource("/config/theme.xml"); }); }) .createFolder("Pictures", (folder) => { @@ -79,107 +79,53 @@ export function loadDefaultData(virtualRoot: VirtualRoot) { }); }); - virtualRoot.createFolder(".github", (folder) => { - folder.createFile("FUNDING", "yml", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/.github/FUNDING.yml"); - }); - }); + // Create files and folders based on repository tree + void fetch("/config/tree.json").then((response) => + response.json() + ).then(({ files, folders }: { files: string[], folders: string[] }) => { - virtualRoot.createFolder(".vscode", (folder) => { - folder.createFile("settings", "json", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/.vscode/settings.json"); - }); - }); + folders.forEach((folderPath) => { + const lastSlashIndex = folderPath.lastIndexOf("/"); - virtualRoot.createFolder("docs", (folder) => { - folder.createFile("README", "md", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/docs/README.md"); - }); - }); + if (lastSlashIndex === -1) { + virtualRoot.createFolder(folderPath); + return; + } - virtualRoot.createFolder("public", (folder) => { - folder.createFolder("assets", (folder) => { - folder.createFolder("apps", (folder) => { - folder.createFolder("icons", (folder) => { - AppsManager.APPS.forEach(({ id }) => { - folder.createFile(id, "svg", (file) => { - file.setSource(AppsManager.getAppIconUrl(id)); - }); - }); - }); - }).createFolder("fonts", (folder) => { - folder.createFolders(["outfit", "roboto-mono"]); - }).createFolder("screenshots", (folder) => { - folder.createFile("screenshot", "png", (file) => { - file.setSource("/assets/screenshots/screenshot-files-info-taskbar-desktop.png"); - }); - }).createFolder("wallpapers", (folder) => { - folder.setProtected(true); - for (let i = 0; i < WALLPAPERS.length; i++) { - const source = WALLPAPERS[i]; - const name = source.split("/").pop().split(".")[0]; - folder.createFile(name, "png", (file) => { - file.setSource(source); - }); + const parentPath = folderPath.substring(0, lastSlashIndex); + const folderName = folderPath.substring(lastSlashIndex + 1); + + const parentFolder = virtualRoot.navigate(parentPath) as VirtualFolder; + parentFolder.createFolder(folderName); + }); + + files.forEach((filePath) => { + const lastSlashIndex = filePath.lastIndexOf("/"); + + const callback = (virtualFile: VirtualFile) => { + console.log(virtualFile.absolutePath); + + const virtualPath = virtualFile.absolutePath; + if (virtualPath.startsWith("/public/")) { + virtualFile.setSource(virtualPath.replace(/^\/public\//, "/")); + } else { + virtualFile.setSource(`https://raw.githubusercontent.com/Prozilla/ProzillaOS/main${virtualPath}`); } - }).createFile("banner", "png", (file) => { - file.setSource("/assets/banner-logo-title.png"); - }).createFile("logo", "svg", (file) => { - file.setSource("/icon.svg"); - }); - }).createFolder("config", (folder) => { - folder.createFile("apps", "xml", (file) => { - file.setSource("/config/apps.xml"); - }).createFile("desktop", "xml", (file) => { - file.setSource("/config/desktop.xml"); - }).createFile("taskbar", "xml", (file) => { - file.setSource("/config/taskbar.xml"); - }).createFile("theme", "xml", (file) => { - file.setSource("/config/theme.xml"); - }); - }).createFolder("documents", (folder) => { - folder.createFile("info", "md", (file) => { - file.setSource("/documents/info.md"); - }).createFile("links", "md", (file) => { - file.setSource("/documents/links.md"); - }); - }).createFile("favicon", "ico", (file) => { - file.setSource("/favicon.ico"); - }).createFile("index", "html", (file) => { - file.setSource("/index.html"); - }).createFile("robots", "txt", (file) => { - file.setSource("/robots.txt"); - }).createFile("sitemap", "xml", (file) => { - file.setSource("/sitemap.xml"); + }; + + if (lastSlashIndex === -1) { + const { name, extension } = VirtualFile.convertId(filePath); + virtualRoot.createFile(name, extension, callback); + return; + } + + const parentPath = filePath.substring(0, lastSlashIndex); + const { name, extension } = VirtualFile.convertId(filePath.substring(lastSlashIndex + 1)); + + const parentFolder = virtualRoot.navigate(parentPath) as VirtualFolder; + parentFolder.createFile(name, extension, callback); }); - }); - - virtualRoot.createFolder("src", (folder) => { - folder.createFolder("components") - .createFolder("config") - .createFolder("features") - .createFolder("hooks") - .createFolder("styles") - .createFile("App", "tsx", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/src/App.tsx"); - }).createFile("index", "tsx", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/src/index"); - }); - }); - - virtualRoot.createFile("", "env", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/.env"); - }).createFile("", "gitignore", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/.gitignore"); - }).createFile("LICENSE", "md", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/LICENSE.md"); - }).createFile("README", "md", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/README.md"); - }).createFile("package", "json", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/package.json"); - }).createFile("deploy", "sh", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/deploy.sh"); - }).createFile("tsconfig", "json", (file) => { - file.setSource("https://raw.githubusercontent.com/Prozilla/ProzillaOS/main/tsconfig.json"); + }).catch(() => { + console.warn("Failed to fetch repository tree. Make sure the tree data is valid and up-to-date using 'npm run fetch'."); }); } \ No newline at end of file diff --git a/src/tools/fetchRepository.ts b/src/tools/fetchRepository.ts new file mode 100644 index 0000000..0a65aaa --- /dev/null +++ b/src/tools/fetchRepository.ts @@ -0,0 +1,65 @@ +import fs from "node:fs"; +import { ANSI } from "../config/apps/terminal.config"; + +const API_URL = "https://api.github.com/"; +const TREE_PATH = "public/config/tree.json"; + +// TO DO: split these two variables into their own file +const OWNER = "Prozilla"; +const REPO = "ProzillaOS"; + +const BRANCH = "main"; + +interface ReponseType { + sha: string; + url: string; + tree: { + path: string; + mode: string; + type: string; + sha: string; + url: string; + }[]; + truncated: boolean; +} + +function fetchRepositoryTree(callback: (tree: string) => void) { + const treeUrl = `${API_URL}repos/${OWNER}/${REPO}/git/trees/${BRANCH}?recursive=true`; + + console.log(`${ANSI.fg.yellow}Fetching: ${ANSI.fg.cyan + treeUrl + ANSI.reset}`); + + void fetch(treeUrl).then((response) => + response.json() + ).then((response: ReponseType) => { + const items = response.tree; + + const files = []; + const folders = []; + + items.forEach(({ path, type }) => { + if (type === "tree") { + folders.push(path); + } else { + files.push(path); + } + }); + + console.log(`Files found: ${ANSI.fg.cyan + files.length + ANSI.reset}`); + console.log(`Folders found: ${ANSI.fg.cyan + folders.length + ANSI.reset}`); + console.log(`Truncated: ${ANSI.fg.cyan + response.truncated.toString() + ANSI.reset}`); + + const tree = JSON.stringify({ files, folders }); + callback(tree); + }).catch((error) => { + console.error(error); + }); +} + +try { + fetchRepositoryTree((tree) => { + fs.writeFileSync(TREE_PATH, tree, { flag: "w+" }); + console.log(`\n${ANSI.fg.green}✓ Generated repository tree: ${ANSI.fg.cyan + TREE_PATH + ANSI.reset}`); + }); +} catch (error) { + console.error(error); +} \ No newline at end of file