diff --git a/.DS_Store b/.DS_Store index b84a0953..ea1d7400 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 2c81904e..4ec85fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,6 @@ filesync */target + +target +Cargo.lock \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 24d7cc6d..e69de29b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +0,0 @@ -{ - "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] -} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..7f032cb5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["./desktop/core"] \ No newline at end of file diff --git a/README.md b/README.md index 7c1eaf94..afaa858b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ _⚠️ the application is still a work in progress, hence, Some features are missing. Please see the [issues](https://github.com/opeolluwa/send-file/issues) page to contribute_ -![screenshot](./filesync.png) +![screenshot](./screenshots/filesync.png) ## Table of Contents diff --git a/components/.DS_Store b/components/.DS_Store deleted file mode 100644 index a1d99fac..00000000 Binary files a/components/.DS_Store and /dev/null differ diff --git a/components/.gitignore b/components/.gitignore deleted file mode 100644 index c6bba591..00000000 --- a/components/.gitignore +++ /dev/null @@ -1,130 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* diff --git a/components/README.md b/components/README.md deleted file mode 100644 index 9e5b2a4b..00000000 --- a/components/README.md +++ /dev/null @@ -1,74 +0,0 @@ - # FileSync Share UI components - -- [Description](#description) -- [Getting Started](#getting-started) - - [Dependencies](#dependencies) - - [Installing](#installing) - - [Executing program](#executing-program) -- [Documentation](#documentation) -- [Help](#help) -- [Authors](#authors) -- [Version History](#version-history) -- [License](#license) -- [Acknowledgments](#acknowledgments) - -## Description - -FileSync Shareed Ui Components - -## Getting Started - -### Dependencies - -- Describe any prerequisites, libraries, OS version, etc., needed before installing program. -- ex. Windows 10 - -### Installing - -- How/where to download your program -- Any modifications needed to be made to files/folders - -### Executing program - -- How to run the program -- Step-by-step bullets - -``` -code blocks for commands -``` - -## Documentation - -Describe any special instructions that are necessary to install a software package on your computer (if applicable). - -## Help - -Any advise for common problems or issues. - -``` -command to run if program contains helper info -``` - -## Authors - -Contributors names and contact info - -ex. Dominique Pizzie -ex. [@DomPizzie](https://twitter.com/dompizzie) - -## Version History - -- 0.2 - - Various bug fixes and optimizations - - See [commit change]() or See [release history]() -- 0.1 - - Initial Release - -## License - -This project is licensed under the [NAME HERE] License - see the LICENSE.md file for details - -## Acknowledgments - -Inspiration, code snippets, etc. - \ No newline at end of file diff --git a/components/index.d.ts b/components/index.d.ts deleted file mode 100644 index c6d9153e..00000000 --- a/components/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module "@filesync/components" \ No newline at end of file diff --git a/components/index.ts b/components/index.ts deleted file mode 100644 index c76745d1..00000000 --- a/components/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Button from "./src/Button"; -import Heading from "./src/Heading"; -import Text from "./src/Text"; -import View from "./src/View"; -import Card from "./src/Card"; -import SearchBar from "./src/Search"; -/** - * https://stackoverflow.com/questions/67696920/vite-rollup-failed-to-resolve-build-error - * resolve the import in target components tust - * - * - * // vite.config.js - * export default defineConfig({ - plugins: [react(), legacy()], - resolve: { - alias: { - "@filesync/components": path.resolve(__dirname, "../components/index.ts"), - "@filesync/types/*": path.resolve(__dirname, "src/pages"), - "@utils": path.resolve(__dirname, "src/utils"), - }, - }, - test: { - globals: true, - environment: "jsdom", - setupFiles: "./src/setupTests.ts", - }, -}); - * - * - * //tsconfig.json - * { - * compilerOptions:{ - * "paths": { -* "@filesync/types/*": ["../desktop/core/bindings/*"], -* "@filesync/components": ["../components"] -* } - * }, - * "include": ["src", "../components/*", "../core/*"], -*} -*/ - -export { Button, Heading, Text, View, Card, SearchBar }; diff --git a/components/package.json b/components/package.json deleted file mode 100644 index 80d8df9c..00000000 --- a/components/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "filesync-components", - "version": "1.0.0", - "description": "FileSync Share UI components", - "main": "index.ts", - "type": "module", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [ - "mobil3", - "desktop", - "wifi", - "react", - "tailwind", - "wifidirect" - ], - "author": "adeoye adefemi ", - "license": "ISC", - "devDependencies": { - "@heroicons/react": "^2.1.3", - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "dependencies": { - "@types/react": "^18.3.1", - "tslib": "^2.6.2", - "typescript": "^5.4.5" - } -} diff --git a/components/src/Button/index.tsx b/components/src/Button/index.tsx deleted file mode 100644 index f7775294..00000000 --- a/components/src/Button/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -interface Props { - children?: React.ReactNode; - className?: string; - onclick?: () => void; -} - -export default function Button({ children, className, onclick }: Props) { - return ( - - ); -} diff --git a/components/src/Card/index.tsx b/components/src/Card/index.tsx deleted file mode 100644 index 14742503..00000000 --- a/components/src/Card/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; -interface CardProps { - style?: React.CSSProperties; - children?: React.ReactNode; - className?: string; -} - -export default function Card({ style, children, className }: CardProps) { - return ( - <> -
- {children} -
- - ); -} diff --git a/components/src/Heading/index.tsx b/components/src/Heading/index.tsx deleted file mode 100644 index 0f304403..00000000 --- a/components/src/Heading/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { Children } from "react"; -interface Props { - children?: React.ReactNode; - context?: string; - className?: string; -} -export default function Heading({ children, className, context }: Props) { - return ( -

- {context || children} -

- ); -} diff --git a/components/src/Search/index.tsx b/components/src/Search/index.tsx deleted file mode 100644 index 4544bdfc..00000000 --- a/components/src/Search/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; -import React, { useState } from "react"; - -/** - * types definition for the search filed - * takes the keyword TODO and the function to execute which will be imported from Rust core - */ -interface Props { - onSearch: (city: string) => void; - placeholder: string; -} - -export default function SearchBar({ onSearch, placeholder }: Props) { - const [city, setCity] = useState(""); - - function handleSubmit(e: { preventDefault: () => void }) { - e.preventDefault(); - onSearch(city); - } - - return ( -
- - setCity(e.target.value)} - /> - -
- ); -} diff --git a/components/src/SearchBar/index.tsx b/components/src/SearchBar/index.tsx deleted file mode 100644 index 801f78f3..00000000 --- a/components/src/SearchBar/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; -import { useState } from "react"; - -/** - * types definition for the search filed - * takes the keyword TODO and the function to execute which will be imported from Rust core - */ -interface Props { - onSearch: (city: string) => void; - placeholder: string; -} - -export default function SearchBar({ onSearch, placeholder }: Props) { - const [city, setCity] = useState(""); - - function handleSubmit(e: { preventDefault: () => void }) { - e.preventDefault(); - onSearch(city); - } - - return ( -
- - setCity(e.target.value)} - /> - -
- ); -} diff --git a/components/src/Text/index.tsx b/components/src/Text/index.tsx deleted file mode 100644 index f47a8af7..00000000 --- a/components/src/Text/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -interface Props { - children?: React.ReactNode; - className?: string; -} -export default function Text({ children, className }: Props) { - return ( -

- {children} -

- ); -} diff --git a/components/src/Thumbnail/assets/archived.png b/components/src/Thumbnail/assets/archived.png deleted file mode 100644 index a31a2540..00000000 Binary files a/components/src/Thumbnail/assets/archived.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/audio.png b/components/src/Thumbnail/assets/audio.png deleted file mode 100644 index 25898f86..00000000 Binary files a/components/src/Thumbnail/assets/audio.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/csv.png b/components/src/Thumbnail/assets/csv.png deleted file mode 100644 index 487f0811..00000000 Binary files a/components/src/Thumbnail/assets/csv.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/default.png b/components/src/Thumbnail/assets/default.png deleted file mode 100644 index 84bc7330..00000000 Binary files a/components/src/Thumbnail/assets/default.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/document.png b/components/src/Thumbnail/assets/document.png deleted file mode 100644 index 04d0a35f..00000000 Binary files a/components/src/Thumbnail/assets/document.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/folder-icon.png b/components/src/Thumbnail/assets/folder-icon.png deleted file mode 100644 index 017e5d05..00000000 Binary files a/components/src/Thumbnail/assets/folder-icon.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/image.png b/components/src/Thumbnail/assets/image.png deleted file mode 100644 index c62b9dcd..00000000 Binary files a/components/src/Thumbnail/assets/image.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/pdf.png b/components/src/Thumbnail/assets/pdf.png deleted file mode 100644 index 4154a4fe..00000000 Binary files a/components/src/Thumbnail/assets/pdf.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/presentation.png b/components/src/Thumbnail/assets/presentation.png deleted file mode 100644 index 9f35df35..00000000 Binary files a/components/src/Thumbnail/assets/presentation.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/svg.png b/components/src/Thumbnail/assets/svg.png deleted file mode 100644 index 2c51ec0e..00000000 Binary files a/components/src/Thumbnail/assets/svg.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/text.png b/components/src/Thumbnail/assets/text.png deleted file mode 100644 index ffa0130b..00000000 Binary files a/components/src/Thumbnail/assets/text.png and /dev/null differ diff --git a/components/src/Thumbnail/assets/video.png b/components/src/Thumbnail/assets/video.png deleted file mode 100644 index d4879b93..00000000 Binary files a/components/src/Thumbnail/assets/video.png and /dev/null differ diff --git a/components/src/Thumbnail/file-type.ts b/components/src/Thumbnail/file-type.ts deleted file mode 100644 index 3c25838a..00000000 --- a/components/src/Thumbnail/file-type.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum FileType { - Image = "image", - Audio = "audio", - PDF = "pdf", - CSV = "csv", - Presentation = "presentation", - Video = "video", - Archive = "archive", - Document = "document", - Text = "text", - SVG = "svg", - Default = "default", -} diff --git a/components/src/Thumbnail/file.ts b/components/src/Thumbnail/file.ts deleted file mode 100644 index 4a16e937..00000000 --- a/components/src/Thumbnail/file.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. - -export interface File { - fileName: string; - fileFormat: string; - filePath: string; - fileSize: string; - isHidden: boolean; - isFolder: boolean; -} diff --git a/components/src/Thumbnail/icon-renderer.tsx b/components/src/Thumbnail/icon-renderer.tsx deleted file mode 100644 index b7551429..00000000 --- a/components/src/Thumbnail/icon-renderer.tsx +++ /dev/null @@ -1,21 +0,0 @@ -// provide file and folder icon -import { getFileIcon } from "./media-icon-maker"; -import folderIcon from "@/assets/common/folder-icon.png"; - - -export interface ThumbnailIconInterface { - fileFormat: string; -} - -export default function ThumbnailIcon({ fileFormat }: ThumbnailIconInterface) { - const thumbnail = getFileIcon(fileFormat).icon; - return ( - file card icon - ); -} diff --git a/components/src/Thumbnail/index.tsx b/components/src/Thumbnail/index.tsx deleted file mode 100644 index f27f6626..00000000 --- a/components/src/Thumbnail/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { File } from "./file"; -import ThumbnailIcon from "./icon-renderer"; - -interface Props extends File {} - -export default function Thumbnail({ - fileName, - fileFormat, - filePath, - fileSize, - isFolder, - isHidden, -}: Props) { - return ( -
- -
-
- {fileName} -
- -
- {fileSize} -
-
-
- ); -} diff --git a/components/src/Thumbnail/media-icon-maker.ts b/components/src/Thumbnail/media-icon-maker.ts deleted file mode 100644 index 77873157..00000000 --- a/components/src/Thumbnail/media-icon-maker.ts +++ /dev/null @@ -1,248 +0,0 @@ -// render the folder icon -import archiveIcon from "./assets/archived.png"; -import audioIcon from "./assets/audio.png"; -import csvIcon from "./assets/csv.png"; -import defaultIcon from "./assets/default.png"; -import documentIcon from "./assets/document.png"; -import imageIcon from "./assets/image.png"; -import pdfIcon from "./assets/pdf.png"; -import presentationIcon from "./assets/presentation.png"; -import svgIcon from "./assets/svg.png"; -import textIcon from "./assets/text.png"; -import videoIcon from "./assets/video.png"; -import { FileType } from "./file-type"; - - -export function getFileIcon(fileExtension: string) { - const imageExtensions = [ - "jpg", - "jpeg", - "png", - "gif", - "bmp", - "tiff", - "raw", - "svg", - "ai", - "eps", - "psd", - "xcf", - "ico", - "webp", - "jxr", - "hdr", - "tif", - "exif", - "pgm", - "ppm", - "pbm", - "pnm", - "heic", - "heif", - "dng", - "cr2", - "nef", - "arw", - "orf", - "rw2", - "sr2", - "raf", - "mrw", - "pef", - "x3f", - "3fr", - "kdc", - "srw", - "nrw", - "rwz", - "rwl", - "iiq", - "rw1", - "r3d", - "fff", - "yuv", - "cin", - "dpx", - "jp2", - "j2k", - "jpf", - "jpx", - "jpm", - "mj2", - "wdp", - "hdp", - "dds", - "pvr", - "tga", - "cur", - "icl", - "thm", - "sai", - "ora", - "pdn", - "kra", - "cpt", - "pdd", - "mng", - "apng", - "svgz", - "emf", - "wmf", - ]; - const documentExtensions = [ - "doc", - "docx", - "rtf", - "odt", - "ods", - "odp", - "odg", - "odp", - "fodp", - "otp", - "doc", - "dot", - "docx", - "docm", - "dotx", - "dotm", - "docb", - "odt", - "fodt", - ]; - const svgExtensions = ["svg"]; - const textExtensions = ["txt"]; - const audioExtensions = [ - "3gp", - "aa", - "aac", - "aax", - "act", - "aiff", - "alac", - "amr", - "ape", - "au", - "awb", - "dss", - "dvf", - "flac", - "gsm", - "iklax", - "ivs", - "m4a", - "m4b", - "m4p", - "mmf", - "movpkg", - "mp3", - "mpc", - "msv", - "nmf", - "ogg", - "oga", - "mogg", - "opus", - "ra", - "rm", - "raw", - "rf64", - "sln", - "tta", - "voc", - "vox", - "wav", - "wma", - "wv", - "webm", - "8svx", - "cda", - ]; - const pdfExtensions = ["pdf"]; - const csvExtensions = ["csv"]; - const presentationExtensions = [ - "ppt", - "pot", - "pps", - "pptx", - "pptm", - "potx", - "potm", - "ppam", - "ppsx", - "ppsm", - "sldx", - "sldm", - "thmx", - ]; - const videoExtensions = [ - "mp4", - "mkv", - "webm", - "flv", - "vob", - "ogv", - "ogg", - "drc", - "gif", - "gifv", - "mng", - "avi", - "MTS", - "MT2S", - "TS", - "mov", - "qt", - "wmv", - "yuv", - "rm", - "rmvb", - "viv", - "asf", - "amv", - "m4p", - "m4v", - "mpg", - "mp2", - "mpeg", - "mpe", - "mpv", - "m2v", - "svi", - "3gp", - "3g2", - "mxf", - "roq", - "nsv", - "f4v", - "f4p", - "f4a", - "f4b", - ]; - const archiveExtensions = ["zip", "rar", "tar", "gz"]; - const extension = fileExtension.toLocaleLowerCase().trim(); - - if (imageExtensions.includes(extension)) { - return { type: FileType.Image, icon: imageIcon }; - } else if (audioExtensions.includes(extension)) { - return { type: FileType.Audio, icon: audioIcon }; - } else if (pdfExtensions.includes(extension)) { - return { type: FileType.PDF, icon: pdfIcon }; - } else if (csvExtensions.includes(extension)) { - return { type: FileType.CSV, icon: csvIcon }; - } else if (presentationExtensions.includes(extension)) { - return { type: FileType.Presentation, icon: presentationIcon }; - } else if (videoExtensions.includes(extension)) { - return { type: FileType.Video, icon: videoIcon }; - } else if (archiveExtensions.includes(extension)) { - return { type: FileType.Archive, icon: archiveIcon }; - } else if (documentExtensions.includes(extension)) { - return { type: FileType.Document, icon: documentIcon }; - } else if (textExtensions.includes(extension)) { - return { type: FileType.Text, icon: textIcon }; - } else if (svgExtensions.includes(extension)) { - return { type: FileType.SVG, icon: svgIcon }; - } else { - return { type: FileType.Default, icon: defaultIcon }; - } -} diff --git a/components/src/View/index.tsx b/components/src/View/index.tsx deleted file mode 100644 index ca1c6451..00000000 --- a/components/src/View/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; - -interface CardProps { - style?: React.CSSProperties; - children?: React.ReactNode; - className?: string; -} - -export default function View({ style, children, className }: CardProps) { - return ( - <> -
- {children} -
- - ); -} diff --git a/components/src/settings/index.ts b/components/src/settings/index.ts deleted file mode 100644 index e2814e4c..00000000 --- a/components/src/settings/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import SettingsTab from "./settings-tab"; -interface SettingsInterface { - icon: any; - text: string; - action?: () => void; - withStyle?: string; - alternateIcon:any, -} -export { SettingsTab }; -export type { SettingsInterface }; diff --git a/components/src/settings/settings-tab.tsx b/components/src/settings/settings-tab.tsx deleted file mode 100644 index d9926f06..00000000 --- a/components/src/settings/settings-tab.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useState } from "react"; -import { SettingsInterface } from "."; - -export default function SettingsTab({ - icon, - text, - action, - withStyle, - alternateIcon -}: SettingsInterface) { - const [currentIcon, setIcon] = useState(icon); - - - return ( -
setIcon(icon)} - onMouseEnter={() => setIcon(alternateIcon)} - // onClick={() => setIcon(alternateIcon)} - onMouseLeave={() => setIcon(icon)} - className={ - "flex items-center gap-4 ease-in-out py-4 first:mt-4 last:border-none last:mb-4 text-gray-500 cursor-pointer pl-4 hover:text-app hover:bg-app-50 rounded-xl" + " "+ - withStyle - } - > - {currentIcon} {text} -
- ); -} diff --git a/components/tsconfig.json b/components/tsconfig.json deleted file mode 100644 index d71e6167..00000000 --- a/components/tsconfig.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/components/yarn.lock b/components/yarn.lock deleted file mode 100644 index 5b7ba825..00000000 --- a/components/yarn.lock +++ /dev/null @@ -1,70 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@heroicons/react@^2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.3.tgz#78a2a7f504a7370283d07eabcddc7fec04f503db" - integrity sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg== - -"@types/prop-types@*": - version "15.7.12" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== - -"@types/react@^18.3.1": - version "18.3.1" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" - integrity sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -loose-envify@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -react-dom@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" - -react@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" - integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== - dependencies: - loose-envify "^1.1.0" - -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" - -tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - -typescript@^5.4.5: - version "5.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" - integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== diff --git a/desktop/core/.gitignore b/desktop/core/.gitignore index 42a2bf57..87b66266 100644 --- a/desktop/core/.gitignore +++ b/desktop/core/.gitignore @@ -1,6 +1,2 @@ # Generated by Cargo # will have compiled files and executables -/target/ - -/views/ -/views/index.html \ No newline at end of file diff --git a/desktop/core/Cargo.toml b/desktop/core/Cargo.toml index 2e16984a..7c7167b1 100644 --- a/desktop/core/Cargo.toml +++ b/desktop/core/Cargo.toml @@ -14,7 +14,7 @@ tauri-build = {version = "1.3", features = [] } [dependencies] assert_fs = "1.0.13" -axum = { version = "0.6.12", features = ["multipart", "headers"] } +axum = { version = "0.6.12", features = ["multipart", "headers", "ws"] } battery = "0.7.8" chrono = "0.4.31" dirs = "5.0.0" @@ -40,7 +40,7 @@ sysinfo = "0.29.2" tauri = {version = "1.2", features = [ "clipboard-write-text", "window-all", "app-all", "dialog-all", "fs-all", "path-all", "shell-open"] } tokio = {version = "1.26.0", features = ["full"] } tokio-util = {version = "0.7", features = ["io"] } -tower = {version = "0.4", features = ["util"] } +tower = {version = "0.4", features = [ "util"] } tower-http = {version = "0.4.0", features = ["fs", "trace", "cors"] } tracing = "0.1.37" tracing-subscriber = {version = "0.3.16", features = ["env-filter"] } @@ -57,7 +57,15 @@ wifidirect-legacy-ap = "0.4.0" aes-gcm = "0.10.3" sha2 = "0.10.8" rand = "0.8.5" +tokio-stream = "0.1" +axum-extra = "0.9.3" +include_dir = "0.7.3" +futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } +headers = "0.3" +tokio-tungstenite = "0.18.0" + [features] # this feature is used for production builds or when `devPath` points to the filesystem # DO NOT REMOVE!! custom-protocol = ["tauri/custom-protocol"] + diff --git a/desktop/core/src/file_manager/file.rs b/desktop/core/src/file_manager/file.rs index e0856571..b37e2318 100644 --- a/desktop/core/src/file_manager/file.rs +++ b/desktop/core/src/file_manager/file.rs @@ -7,9 +7,6 @@ use walkdir::DirEntry; extern crate dirs; use path_absolutize::*; - - - /// a function to compute file size /// accept files size in byte and parse it to human readable KB, MB, TB, GB e.t. pub fn compute_file_size(size: u128) -> String { @@ -26,8 +23,6 @@ pub fn compute_file_size(size: u128) -> String { } } - - #[derive(serde::Serialize, Debug)] pub struct DriveInformation { name: String, @@ -44,7 +39,6 @@ pub struct Drives { array_of_drives: Vec, } - // the file structure #[derive(Debug, Default, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] diff --git a/desktop/core/src/file_manager/mod.rs b/desktop/core/src/file_manager/mod.rs index 3fb6163c..24a7a7c2 100644 --- a/desktop/core/src/file_manager/mod.rs +++ b/desktop/core/src/file_manager/mod.rs @@ -1,7 +1,5 @@ pub mod file; -use std::net::Ipv4Addr; -use std::path::PathBuf; use crate::UPLOAD_PATH; use crate::{ file_manager::file::{get_files_in_directory, File}, @@ -10,6 +8,8 @@ use crate::{ use dirs; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use std::net::Ipv4Addr; +use std::path::PathBuf; use ts_rs::TS; use crate::database::{self, TransferHistory, TransferHistoryBuilder}; diff --git a/desktop/core/src/ipc_manager/mod.rs b/desktop/core/src/ipc_manager/mod.rs index f1fef474..e4f34b47 100644 --- a/desktop/core/src/ipc_manager/mod.rs +++ b/desktop/core/src/ipc_manager/mod.rs @@ -1,4 +1,3 @@ - pub mod settings; pub mod utils; pub mod wifi; diff --git a/desktop/core/src/main.rs b/desktop/core/src/main.rs index 97c30559..9f3e87cb 100644 --- a/desktop/core/src/main.rs +++ b/desktop/core/src/main.rs @@ -2,14 +2,16 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] extern crate uptime_lib; +use crate::file_manager::get_transfer_history; use crate::file_manager::read_dir; use crate::ipc_manager::settings::{get_application_data, get_settings, update_settings}; use crate::ipc_manager::utils::{ generate_qr_code, get_ip_address, get_system_information, is_connected_to_wifi, }; +use include_dir::include_dir; +use include_dir::Dir; use lazy_static::lazy_static; use server::http_server; -use crate::file_manager::get_transfer_history; mod database; mod file_manager; mod ipc_manager; @@ -18,6 +20,8 @@ mod server; mod state_manager; mod utils; +pub const STATIC_ASSETS_DIRECTORY: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/views"); + lazy_static! { /** * the lazy static crate allow the lazy evaluation of constants thus, one can bypass the impossible dynamic bindings of constants @@ -30,6 +34,9 @@ lazy_static! { pub static ref UPLOAD_DIRECTORY: std::string::String = String::from("filesync"); + // the static files directory + + //create wi-share directory in the downloads path dir and / save files to $DOWNLOADS/wi-share pub static ref UPLOAD_PATH : std::string::String = { let os_default_downloads_dir = dirs::download_dir().unwrap(); diff --git a/desktop/core/src/network_manager/linux.rs b/desktop/core/src/network_manager/linux.rs index e69de29b..790370b8 100644 --- a/desktop/core/src/network_manager/linux.rs +++ b/desktop/core/src/network_manager/linux.rs @@ -0,0 +1,25 @@ + + +use super::WifiHotspotConfig; +use super::WifiHotspotConfigBuilder; +impl WifiHotspotConfigBuilder for WifiHotspotConfig { + fn create() -> Self { + todo!() + } + + fn connect() -> crate::utils::CommandData { + todo!() + } + + fn scan() -> Vec { + todo!() + } + + fn stop() -> () { + todo!() + } + + fn refresh() -> () { + todo!() + } +} \ No newline at end of file diff --git a/desktop/core/src/network_manager/mac.rs b/desktop/core/src/network_manager/mac.rs index e69de29b..713ecab0 100644 --- a/desktop/core/src/network_manager/mac.rs +++ b/desktop/core/src/network_manager/mac.rs @@ -0,0 +1,25 @@ + +use super::WifiHotspotConfig; +use super::WifiHotspotConfigBuilder; + +impl WifiHotspotConfigBuilder for WifiHotspotConfig { + fn create() -> Self { + todo!() + } + + fn connect() -> crate::utils::CommandData { + todo!() + } + + fn scan() -> Vec { + todo!() + } + + fn stop() -> () { + todo!() + } + + fn refresh() -> () { + todo!() + } +} \ No newline at end of file diff --git a/desktop/core/src/network_manager/mod.rs b/desktop/core/src/network_manager/mod.rs index 71135721..92ee4221 100644 --- a/desktop/core/src/network_manager/mod.rs +++ b/desktop/core/src/network_manager/mod.rs @@ -1,14 +1,30 @@ #[allow(unused)] - use std::fmt; -use crate::utils::system_info::SystemInformation; -use nanoid::nanoid; use serde::{Deserialize, Serialize}; +use crate::utils::CommandData; -///TODO: use conditional import -// pub mod hotspot; pub mod ip_manager; + +#[cfg_attr(target_os = "linux", path = "linux.rs")] +#[cfg_attr(target_os = "macos", path = "mac.rs")] +#[cfg_attr(target_os = "windows", path = "windows.rs")] +pub mod network; + + +/// the Network config trait to be implemented independently fi=or windows, mac and linux +pub trait WifiHotspotConfigBuilder { + /// create new hotspot + fn create() -> Self; + /// connect to a wifi + fn connect() -> CommandData; +/// scan for network, return an array of network names / ssid +fn scan() -> Vec; +/// terminate the wifi hot spot +fn stop() -> (); +/// refresh the network hotspot or wifi +fn refresh() -> (); +} // pub mod network_scanner; /// the network interface type contains the /// - gateway ex 192.168.0.0.1 @@ -17,18 +33,18 @@ pub mod ip_manager; /// - (optional) QR code to scan to connect /// - (optional) the network card frequency, e.g 2.4GHz or 5Ghz /// it will be used to communicate with the application interface -#[derive(Debug, Serialize, Deserialize, Default)] +#[derive(Debug, Serialize, Deserialize)] pub struct WifiHotspotConfig { - /// the network broadcast ip address + /// the network broadcast ip address eg 192.16.8.0.1 gateway: String, - /// the network name (generates) + /// the network name (generated) ssid: String, /// the network password password: String, /// the status of the network - status: Option, - /// message - message: Option, + status: NetworkAccessStatus, + /// success or error message + message: String, } /// network was successfully created or there is an error @@ -38,53 +54,7 @@ pub enum NetworkAccessStatus { Error, } -#[allow(unused)] - -impl WifiHotspotConfig { - - pub fn new(gateway: &str) -> Self { - let SystemInformation { - system_name: ssid, .. - } = SystemInformation::new(); // use the system name - let password = nanoid!(8); // generate username and password - let gateway = gateway.to_string(); - - Self { - gateway, - ssid, - password, - ..Default::default() - } - } - - #[allow(dead_code)] - pub fn ok(gateway: &str) -> Self { - let SystemInformation { - system_name: ssid, .. - } = SystemInformation::new(); // use the system name - let password = nanoid!(8); // generate username and password - let gateway = gateway.to_string(); - let message = Some(String::from("Wifi hotspot created successfully")); - - Self { - gateway, - ssid, - password, - status: Some(NetworkAccessStatus::Created), - message, - } - } - - pub fn err() -> Self { - let message = Some(String::from("Failed to create Wifi hotspot")); - - Self { - status: Some(NetworkAccessStatus::Created), - message, - ..Default::default() - } - } -} +impl WifiHotspotConfig {} impl fmt::Display for WifiHotspotConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/desktop/core/src/network_manager/windows.rs b/desktop/core/src/network_manager/windows.rs index e69de29b..3c14d3a2 100644 --- a/desktop/core/src/network_manager/windows.rs +++ b/desktop/core/src/network_manager/windows.rs @@ -0,0 +1,26 @@ + + +use super::WifiHotspotConfig; +use super::WifiHotspotConfigBuilder; + +impl WifiHotspotConfigBuilder for WifiHotspotConfig { + fn create() -> Self { + todo!() + } + + fn connect() -> crate::utils::CommandData { + todo!() + } + + fn scan() -> Vec { + todo!() + } + + fn stop() -> () { + todo!() + } + + fn refresh() -> () { + todo!() + } +} \ No newline at end of file diff --git a/desktop/core/src/server/http_server.rs b/desktop/core/src/server/http_server.rs index 5002a2ab..348a61e5 100644 --- a/desktop/core/src/server/http_server.rs +++ b/desktop/core/src/server/http_server.rs @@ -1,3 +1,4 @@ + use reqwest::Method; use tower_http::cors::Any; @@ -10,7 +11,6 @@ use axum::extract::DefaultBodyLimit; use crate::database::Database; use crate::server::router; -use crate::server::routes::handle_404; use crate::SERVER_PORT; /** @@ -53,14 +53,14 @@ pub async fn core_server() { .parse::() .expect("invalid socket address"); - println!(" the server port is http://{}", ip_address); + tracing::debug!(" the server port is http://{}", ip_address); + + - // build our application with the required routes let app = router::app() .layer(file_limit) .layer(cors_layer) - .layer(tower_http::trace::TraceLayer::new_for_http()) - .fallback(handle_404); + .layer(tower_http::trace::TraceLayer::new_for_http()); // run the server axum::Server::bind(&ip_address) diff --git a/desktop/core/src/server/router.rs b/desktop/core/src/server/router.rs index 20f8d789..9f9bd2c5 100644 --- a/desktop/core/src/server/router.rs +++ b/desktop/core/src/server/router.rs @@ -1,22 +1,36 @@ // return all the routes as /api/ + use axum::{ routing::{get, post}, Router, }; +use hyper::{Body, Request}; +use tower::ServiceExt; +use tower_http::services::ServeDir; + use super::routes::{ - accept_file_upload, download_file, file_upload_form, get_file, health_check, ping_server, - system_information, + accept_file_upload, download_file, get_file, handle_404, health_check, + ping_server, system_information, ws_handler, }; // the app is moved here to allow sharing across test modules pub fn app() -> Router { + Router::new() .route("/", get(ping_server)) - .route("/upload", post(accept_file_upload).get(file_upload_form)) + .route("/upload", post(accept_file_upload)) .route("/health", post(accept_file_upload).get(health_check)) .route("/api/sys-info", get(system_information)) .route("/api/download", get(download_file)) .route("/api/file", get(get_file)) + .route("/notify", get(ws_handler)) + .nest_service( + "/view", + get(|request: Request| async { + ServeDir::new("views").oneshot(request).await + }), + ) + .fallback(handle_404) } diff --git a/desktop/core/src/server/routes.rs b/desktop/core/src/server/routes.rs index 99fdc479..1cd75ed3 100644 --- a/desktop/core/src/server/routes.rs +++ b/desktop/core/src/server/routes.rs @@ -1,19 +1,22 @@ +use axum::body::{Bytes, StreamBody}; +use axum::extract::ws::{CloseFrame, Message, WebSocket}; +use axum::extract::{ConnectInfo, Multipart, WebSocketUpgrade}; use axum::response::Html; -use axum::{BoxError, Json}; -// get documents, audio, video, e.t.c and render the in the browser use axum::{extract::Query, http::StatusCode, response::IntoResponse}; -use futures::{Stream, TryStreamExt}; +use axum::{headers, BoxError, Json, TypedHeader}; +use futures::stream::{Stream, TryStreamExt}; +use futures::{sink::SinkExt, stream::StreamExt}; use hyper::header; use serde::{Deserialize, Serialize}; -use tokio::fs::File; -use tokio::io::{self, BufWriter}; -use tokio_util::io::{ReaderStream, StreamReader}; - -use axum::body::{Bytes, StreamBody}; -use axum::extract::Multipart; use serde_json::json; use serde_json::Value; +use std::borrow::Cow; use std::fs; +use std::net::SocketAddr; +use std::ops::ControlFlow; +use tokio::fs::File; +use tokio::io::{self, BufWriter}; +use tokio_util::io::{ReaderStream, StreamReader}; use crate::utils::{system_info::SystemInformation, CommandData}; use crate::UPLOAD_DIRECTORY; @@ -64,229 +67,8 @@ pub async fn system_information() -> (StatusCode, Json Html<&'static str> { - Html( - r#" - - - - - - Document - - - -
-
File Uploader
-
- - -

Browse File to Upload

-
-
-
-
- - - - - - "#, - ) -} - /// return an html page to receive file upload -pub async fn file_upload_form() -> Html<&'static str> { +pub async fn _file_upload_form() -> Html<&'static str> { Html( r#" @@ -544,3 +326,187 @@ mod basic_endpoints { // println!(" the not-found-endpoint response is {response:?}"); } } + +/// use Server sent event to notify client +// pub async fn notify_peer( +// TypedHeader(user_agent): TypedHeader, +// ) -> Sse>> { +// println!("`{}` connected", user_agent.as_str()); + +// // A `Stream` that repeats an event every second +// let stream = stream::repeat_with(|| Event::default().data("hi!")) +// .map(Ok) +// .throttle(Duration::from_secs(1)); + +// Sse::new(stream).keep_alive( +// axum::response::sse::KeepAlive::new() +// .interval(Duration::from_secs(1)) +// .text("keep-alive-text"), +// ) +// } + +/// The handler for the HTTP request (this gets called when the HTTP GET lands at the start +/// of websocket negotiation). After this completes, the actual switching from HTTP to +/// websocket protocol will occur. +/// This is the last point where we can extract TCP/IP metadata such as IP address of the client +/// as well as things from HTTP headers such as user-agent of the browser etc. +pub async fn ws_handler( + ws: WebSocketUpgrade, + user_agent: Option>, + ConnectInfo(addr): ConnectInfo, +) -> impl IntoResponse { + let user_agent = if let Some(TypedHeader(user_agent)) = user_agent { + user_agent.to_string() + } else { + String::from("Unknown browser") + }; + println!("`{user_agent}` at {addr} connected."); + // finalize the upgrade process by returning upgrade callback. + // we can customize the callback by sending additional info such as address. + ws.on_upgrade(move |socket| handle_socket(socket, addr)) +} + +/// Actual websocket statemachine (one will be spawned per connection) +async fn handle_socket(mut socket: WebSocket, who: SocketAddr) { + //send a ping (unsupported by some browsers) just to kick things off and get a response + if socket.send(Message::Ping(vec![1, 2, 3])).await.is_ok() { + println!("Pinged {}...", who); + } else { + println!("Could not send ping {}!", who); + // no Error here since the only thing we can do is to close the connection. + // If we can not send messages, there is no way to salvage the statemachine anyway. + return; + } + + // receive single message from a client (we can either receive or send with socket). + // this will likely be the Pong for our Ping or a hello message from client. + // waiting for message from a client will block this task, but will not block other client's + // connections. + if let Some(msg) = socket.recv().await { + if let Ok(msg) = msg { + if process_message(msg, who).is_break() { + return; + } + } else { + println!("client {who} abruptly disconnected"); + return; + } + } + + // Since each client gets individual statemachine, we can pause handling + // when necessary to wait for some external event (in this case illustrated by sleeping). + // Waiting for this client to finish getting its greetings does not prevent other clients from + // connecting to server and receiving their greetings. + for i in 1..5 { + if socket + .send(Message::Text(format!("Hi {i} times!"))) + .await + .is_err() + { + println!("client {who} abruptly disconnected"); + return; + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + + // By splitting socket we can send and receive at the same time. In this example we will send + // unsolicited messages to client based on some sort of server's internal event (i.e .timer). + let (mut sender, mut receiver) = socket.split(); + + // Spawn a task that will push several messages to the client (does not matter what client does) + let mut send_task = tokio::spawn(async move { + let n_msg = 20; + for i in 0..n_msg { + // In case of any websocket error, we exit. + if sender + .send(Message::Text(format!("Server message {i} ..."))) + .await + .is_err() + { + return i; + } + + tokio::time::sleep(std::time::Duration::from_millis(300)).await; + } + + println!("Sending close to {who}..."); + if let Err(e) = sender + .send(Message::Close(Some(CloseFrame { + code: axum::extract::ws::close_code::NORMAL, + reason: Cow::from("Goodbye"), + }))) + .await + { + println!("Could not send Close due to {}, probably it is ok?", e); + } + n_msg + }); + + // This second task will receive messages from client and print them on server console + let mut recv_task = tokio::spawn(async move { + let mut cnt = 0; + while let Some(Ok(msg)) = receiver.next().await { + cnt += 1; + // print message and break if instructed to do so + if process_message(msg, who).is_break() { + break; + } + } + cnt + }); + + // If any one of the tasks exit, abort the other. + tokio::select! { + rv_a = (&mut send_task) => { + match rv_a { + Ok(a) => println!("{} messages sent to {}", a, who), + Err(a) => println!("Error sending messages {:?}", a) + } + recv_task.abort(); + }, + rv_b = (&mut recv_task) => { + match rv_b { + Ok(b) => println!("Received {} messages", b), + Err(b) => println!("Error receiving messages {:?}", b) + } + send_task.abort(); + } + } + + // returning from the handler closes the websocket connection + println!("Websocket context {} destroyed", who); +} + +/// helper to print contents of messages to stdout. Has special treatment for Close. +fn process_message(msg: Message, who: SocketAddr) -> ControlFlow<(), ()> { + match msg { + Message::Text(t) => { + println!(">>> {} sent str: {:?}", who, t); + } + Message::Binary(d) => { + println!(">>> {} sent {} bytes: {:?}", who, d.len(), d); + } + Message::Close(c) => { + if let Some(cf) = c { + println!( + ">>> {} sent close with code {} and reason `{}`", + who, cf.code, cf.reason + ); + } else { + println!(">>> {} somehow sent close message without CloseFrame", who); + } + return ControlFlow::Break(()); + } + + Message::Pong(v) => { + println!(">>> {} sent pong with {:?}", who, v); + } + // You should never need to manually handle Message::Ping, as axum's websocket library + // will do so for you automagically by replying with Pong and copying the v according to + // spec. But if you need the contents of the pings you can see them here. + Message::Ping(v) => { + println!(">>> {} sent ping with {:?}", who, v); + } + } + ControlFlow::Continue(()) +} diff --git a/desktop/core/src/utils/mod.rs b/desktop/core/src/utils/mod.rs index 92bbb208..faba9609 100644 --- a/desktop/core/src/utils/mod.rs +++ b/desktop/core/src/utils/mod.rs @@ -1,4 +1,3 @@ -use rand::Rng; use serde::{Deserialize, Serialize}; use std::fmt::{self}; use std::fs::File; @@ -80,23 +79,3 @@ pub fn _verify_file_openable(file: &PathBuf) -> Result<(), String> { } Ok(()) } - - -/// generate password fro wifi hotspot -pub fn _generate_password() -> String { - let mut rng = rand::thread_rng(); - let chars: Vec = "0123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" - .chars() - .collect(); - const PASSWORD_LENGTH: usize = 8; - let mut password: Vec = vec!['\0'; PASSWORD_LENGTH]; - for i in 0..PASSWORD_LENGTH { - let current_char_index = rng.gen_range(0..chars.len()); - password[i] = chars[current_char_index]; - } - String::from_iter(password) -} - - - - diff --git a/desktop/core/src/utils/system_info.rs b/desktop/core/src/utils/system_info.rs index 45c9979b..eb7a768a 100644 --- a/desktop/core/src/utils/system_info.rs +++ b/desktop/core/src/utils/system_info.rs @@ -1,5 +1,8 @@ use std::{fmt, net::Ipv4Addr}; +use crate::file_manager::file::compute_file_size; +use crate::network_manager::ip_manager; +use crate::SERVER_PORT; use battery::units::time::*; use battery::Manager; use mockall::predicate::*; @@ -10,11 +13,6 @@ use std::collections::HashSet; use sys_info; use sysinfo::{DiskExt, System, SystemExt}; use ts_rs::TS; - use crate::file_manager::file::compute_file_size; -use crate::network_manager::ip_manager; -use crate::SERVER_PORT; - - #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[ts(export)] diff --git a/desktop/core/tauri.conf.json b/desktop/core/tauri.conf.json index 3d199917..6029cc18 100644 --- a/desktop/core/tauri.conf.json +++ b/desktop/core/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "filesync", - "version": "0.7.6" + "version": "0.7.7" }, "tauri": { "allowlist": { diff --git a/desktop/next.config.js b/desktop/next.config.js index 471e4ab6..0c7b23ba 100644 --- a/desktop/next.config.js +++ b/desktop/next.config.js @@ -6,10 +6,7 @@ const nextConfig = { // See https://nextjs.org/docs/messages/export-image-api for different workarounds. images: { unoptimized: true, - }, - experimental: { - externalDir: true, - }, + } }; module.exports = nextConfig; diff --git a/desktop/package.json b/desktop/package.json index 56748c37..a79cc5e9 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -36,6 +36,7 @@ "remixicon": "^4.2.0", "tauri-plugin-sqlite-api": "github:lzdyes/tauri-plugin-sqlite#v0.1.1", "tauri-plugin-store-api": "https://github.com/tauri-apps/tauri-plugin-store#v1", + "video.js": "^8.12.0", "viewerjs": "^1.11.6" }, "devDependencies": { diff --git a/desktop/src/components/MemoryInformation.tsx b/desktop/src/components/MemoryInformation.tsx index de77bf19..675e2ba2 100644 --- a/desktop/src/components/MemoryInformation.tsx +++ b/desktop/src/components/MemoryInformation.tsx @@ -3,19 +3,18 @@ import { useContext } from "react"; // use this to display the available memory export const MemoryInformation = ({ - systemName, - usedMemory, - totalMemory, + usedDisk, + availableDisk, }: { - systemName: string; - usedMemory: string; - totalMemory: string; + usedDisk: string; + availableDisk: string; }) => { const { data: isConnectedToWifi } = useContext(WifiStatusContext); - const freeMemory = - Number(totalMemory?.split(" ")[0]) - Number(usedMemory?.split(" ")[0]); - const memBarWidth = "56%"; + const usedMemory = Number(usedDisk?.split(" ")[0]); + const freeMemory = Number(availableDisk?.split(" ")[0]); + const totalMemory = usedMemory + freeMemory; + const memoryBarWidth = Math.round((freeMemory / totalMemory) * 100); return (
- {usedMemory} of {totalMemory} + {availableDisk} free space
@@ -37,7 +36,7 @@ export const MemoryInformation = ({ ? "bg-app-400 h-2 rounded-full" : "bg-gray-400 h-2 rounded-full" } - style={{ width: memBarWidth }} + style={{ width: `${memoryBarWidth}%` }} >
diff --git a/desktop/src/components/app/AppAside.tsx b/desktop/src/components/app/AppAside.tsx index f449e276..73cc1be3 100644 --- a/desktop/src/components/app/AppAside.tsx +++ b/desktop/src/components/app/AppAside.tsx @@ -8,7 +8,7 @@ export default function Aside() { const { fileList } = useContext(FileContext); return ( -