delete website (move to another repository)

This commit is contained in:
MedzikUser 2022-06-15 21:10:00 +02:00
parent 50ed01597f
commit 5c355cfdaf
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
44 changed files with 15 additions and 1656 deletions

View File

@ -2,14 +2,10 @@ name: Build release binaries (and publish them if this is a tag)
on:
push:
paths:
- '**'
- '!website/**'
branches:
- main
# pull_request:
# paths:
# - '**'
# - '!website/**'
workflow_dispatch:

View File

@ -4,14 +4,8 @@ on:
push:
branches:
- main
paths:
- '**'
- '!website/**'
pull_request:
paths:
- '**'
- '!website/**'
workflow_dispatch:
@ -49,7 +43,7 @@ jobs:
path: |
~/.cargo/registry/cache/
target/
key: build-${{ runner.os }}-${{ matrix.rust }}-rust-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-${{ matrix.rust }}-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}
- name: cargo build
uses: actions-rs/cargo@v1
@ -62,36 +56,6 @@ jobs:
command: clippy
args: --no-deps -- -D warnings
test:
strategy:
fail-fast: false
matrix:
rust: [stable, nightly]
name: ${{ matrix.rust }} test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Rust toolchain
id: rust-toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Cache
uses: actions/cache@v3
id: cache
with:
path: |
~/.cargo/registry/cache/
target/
key: test-${{ runner.os }}-${{ matrix.rust }}-rust-${{ steps.rust-toolchain.outputs.rustc_hash }}-${{ hashFiles('**/Cargo.lock') }}
- name: cargo test
uses: actions-rs/cargo@v1
with:

View File

@ -1,70 +0,0 @@
name: Website
on:
push:
branches:
- main
paths:
- 'website/**'
pull_request:
paths:
- 'website/**'
workflow_dispatch:
jobs:
build:
name: Next.js Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
- name: Install pnpm
uses: pnpm/action-setup@v2
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- name: Cache pnpm
uses: actions/cache@v3
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('website/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Cache Next.js
uses: actions/cache@v3
with:
path: |
${{ github.workspace }}/website/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('website/pnpm-lock.yaml') }}-${{ hashFiles('website/**.[jt]s', 'website/**.[jt]sx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('website/pnpm-lock.yaml') }}-
- name: Install dependencies
run: pnpm install --prefix website
- name: Build page
run: pnpm --prefix website run build
- name: Export page
run: pnpm --prefix website run export
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: website-static
path: website/out/**

View File

@ -1,47 +1,31 @@
# HomeDisk cloud server
[![docs-rs]](https://homedisk-doc.vercel.app)
[![total-lines]](https://github.com/MedzikUser/HomeDisk)
[![code-size]](https://github.com/MedzikUser/HomeDisk)
[![CI]](https://github.com/MedzikUser/HomeDisk/actions/workflows/rust.yml)
[docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
[total-lines]: https://img.shields.io/tokei/lines/github/MedzikUser/HomeDisk?style=for-the-badge&logo=github&color=fede00
[code-size]: https://img.shields.io/github/languages/code-size/MedzikUser/HomeDisk?style=for-the-badge&color=c8df52&logo=github
[CI]: https://img.shields.io/github/workflow/status/MedzikUser/rust-crypto-utils/Rust/main?style=for-the-badge
![](https://i.imgur.com/fOtiSf7.png)
[home-screenshot]: https://cdn.medzik.xyz/fz4QGfS.png
[login-screenshot]: https://cdn.medzik.xyz/vo10bes.png
![](https://i.imgur.com/vLautmq.png)
[![docs-rs]](https://homedisk-doc.vercel.app)
[![total-lines]](https://github.com/MedzikUser/HomeDisk)
[![code-size]](https://github.com/MedzikUser/HomeDisk)
[![CI]](https://github.com/MedzikUser/HomeDisk/actions/workflows/rust.yml)
![home-screenshot]
![login-screenshot]
## 👨‍💻 Building
First clone the repository: `git clone git@github.com:MedzikUser/HomeDisk.git`
First clone the repository: `git clone https://github.com/MedzikUser/HomeDisk.git`
### Server
#### Requirements
### Requirements
- Rust
To build run the command: `cargo build --release`
The compiled binary can be found in `./target/release/cloud`
### Website
#### Requirements
- Node.js
- pnpm
Run these commands to build:
- Go to directory `./website`
- Install dependencies: `pnpm install`
- Build website: `pnpm run build`
- Export website to static HTML files: `pnpm run export` (Optional)
If you exported the page to HTML files, they are located in the `./out` directory,
if not, you can start the site with `pnpm run start`
The compiled binary can be found in `./target/release/homedisk`
## 🖴 Creating tables in a SQLite database

View File

@ -1,12 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false

View File

@ -1,18 +0,0 @@
{
"extends": [
"eslint:recommended",
"next/core-web-vitals"
],
"rules": {
"semi": [
"warn",
"never"
],
"no-unused-vars": [
"warn"
],
"no-unused-expressions": [
"warn"
]
}
}

34
website/.gitignore vendored
View File

@ -1,34 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

View File

@ -1,8 +0,0 @@
import axios from "axios"
import config from "../config"
const instance = axios.create({
baseURL: config.apiUrl,
})
export default instance

View File

@ -1,34 +0,0 @@
import axios from './axios'
export default async function createDir(path: string, token: string): Promise<any> {
const request = axios.post("/fs/createdir", {
path
}, {
headers: {
Authorization: `Bearer ${token}`,
}
})
const response = request
.then(response => {
const { data } = response
return data
})
.catch(err => {
if (err.response?.data?.error_message) {
const error = err.response.data.error_message
if (error.toString() == "[object Object]") {
Object.keys(error).forEach(key => {
throw new Error(key)
})
}
throw new Error(error)
}
throw new Error(err)
})
return response
}

View File

@ -1,9 +0,0 @@
import list from "./list"
import login from "./login"
import register from "./register"
import upload from "./upload"
import createDir from "./create-directory"
const api = { list, login, register, upload, createDir }
export default api

View File

@ -1,34 +0,0 @@
import axios from './axios'
export default async function list(path: string, token: string): Promise<any> {
const request = axios.post("/fs/list", {
path
}, {
headers: {
Authorization: `Bearer ${token}`,
}
})
const response = request
.then(response => {
const { data } = response
return data
})
.catch(err => {
if (err.response?.data?.error_message) {
const error = err.response.data.error_message
if (error.toString() == "[object Object]") {
Object.keys(error).forEach(key => {
throw new Error(key)
})
}
throw new Error(error)
}
throw new Error(err)
})
return response
}

View File

@ -1,32 +0,0 @@
import axios from './axios'
export default async function login(username: string, password: string): Promise<string> {
const request = axios.post("/auth/login", {
username,
password,
})
const response = request
.then(response => {
const { data } = response
return data.LoggedIn.access_token
})
.catch(err => {
if (err.response?.data?.error_message) {
const error = err.response.data.error_message
if (error.toString() == "[object Object]") {
Object.keys(error).forEach(key => {
throw new Error(key)
})
}
throw new Error(error)
}
throw new Error(err)
})
return response
}

View File

@ -1,32 +0,0 @@
import axios from './axios'
export default async function register(username: string, password: string): Promise<string> {
const request = axios.post("/auth/register", {
username,
password,
})
const response = request
.then(response => {
const { data } = response
return data.LoggedIn.access_token
})
.catch(err => {
if (err.response?.data?.error_message) {
const error = err.response.data.error_message
if (error.toString() == "[object Object]") {
Object.keys(error).forEach(key => {
throw new Error(key)
})
}
throw new Error(error)
}
throw new Error(err)
})
return response
}

View File

@ -1,32 +0,0 @@
import axios from './axios'
export default async function list(path: string, formData: FormData, token: string): Promise<any> {
const request = axios.post(`/fs/upload?path=${path}`, formData, {
headers: {
Authorization: `Bearer ${token}`,
}
})
const response = request
.then(response => {
const { data } = response
return data
})
.catch(err => {
if (err.response?.data?.error_message) {
const error = err.response.data.error_message
if (error.toString() == "[object Object]") {
Object.keys(error).forEach(key => {
throw new Error(key)
})
}
throw new Error(error)
}
throw new Error(err)
})
return response
}

View File

@ -1,9 +0,0 @@
import { Button } from "@mui/material"
import styled from "styled-components"
const SubmitButton = styled(Button)`
margin-top: 1rem;
align-content: "center";
`
export default SubmitButton

View File

@ -1,7 +0,0 @@
import styled from "styled-components"
const ErrorComponent = styled.div`
color: ${({ theme }) => theme.colors.error};
`
export default ErrorComponent

View File

@ -1,35 +0,0 @@
import styled from "styled-components"
const Title = styled.h1`
margin: 0;
line-height: 1.15;
font-size: 2rem;
text-align: center;
margin-bottom: 1rem;
a {
color: ${({ theme }) => theme.pages.index.title.a};
text-decoration: none;
animation: animate 1.5s linear infinite;
}
@keyframes animate {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
a:hover,
a:focus,
a:active {
text-decoration: underline;
}
`
export default Title

View File

@ -1,14 +0,0 @@
import styled from 'styled-components'
const Container = styled.div`
height: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.colors.background};
color: ${({ theme }) => theme.colors.color};
`
export default Container

View File

@ -1,33 +0,0 @@
import { GitHub } from "@mui/icons-material"
import { IconButton } from "@mui/material"
import styled from "styled-components"
import { links } from "../config"
const StyledFooter = styled.footer`
width: 100%;
height: 100px;
border-top: 1px solid ${({ theme }) => theme.footer.borderTop};
display: flex;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.colors.background};
color: ${({ theme }) => theme.colors.color};
a {
display: flex;
justify-content: center;
align-items: cente;
}
`
export default function Footer() {
return (
<StyledFooter>
<IconButton color="inherit">
<a href={links.github} target="_blank" rel="noreferrer" color="inherit">
<GitHub />
</a>
</IconButton>
</StyledFooter>
)
}

View File

@ -1,45 +0,0 @@
import { faMoon, faSignOut, faSun } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { RocketLaunch } from "@mui/icons-material"
import { AppBar, IconButton, Link, Stack, Toolbar, Typography } from "@mui/material"
export default function Footer({ toggleTheme, theme}: Props) {
return (
<AppBar
position="static"
sx={{ marginBottom: "calc(2% + 10px)" }}
>
<Toolbar>
<Link href="/" color="inherit">
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="logo"
>
<RocketLaunch />
</IconButton>
</Link>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
HomeDisk
</Typography>
<Stack direction="row" spacing={2}>
<IconButton onClick={() => toggleTheme()}>
<FontAwesomeIcon icon={theme == "light" ? faMoon : faSun} />
</IconButton>
<IconButton>
<FontAwesomeIcon icon={faSignOut} />
</IconButton>
</Stack>
</Toolbar>
</AppBar>
)
}
type Props = {
toggleTheme: () => any,
theme: string
}

View File

@ -1,49 +0,0 @@
import styled from "styled-components"
const Card = styled.div`
margin: 1rem;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
max-width: 300px;
:hover,
:focus,
:active {
color: #0070f3;
border-color: #0070f3;
}
h2 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
`
const CardSignIn = styled.a`
color: ${({ theme }) => theme.pages.index.cards.signin};
`
const CardRegister = styled.a`
color: ${({ theme }) => theme.pages.index.cards.register};
`
const CardFiles = styled.a`
color: ${({ theme }) => theme.pages.index.cards.files};
`
const CardSettings = styled.a`
color: ${({ theme }) => theme.pages.index.cards.settings};
`
export { Card, CardSignIn, CardRegister, CardFiles, CardSettings }

View File

@ -1,9 +0,0 @@
import styled from "styled-components"
const Description = styled.p`
line-height: 1.5;
font-size: 1.5rem;
text-align: center;
`
export default Description

View File

@ -1,11 +0,0 @@
import styled from "styled-components"
const Grid = styled.div`
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
`
export default Grid

View File

@ -1,34 +0,0 @@
import styled from "styled-components"
const Title = styled.h1`
margin: 0;
line-height: 1.15;
font-size: 4rem;
text-align: center;
a {
color: ${({ theme }) => theme.pages.index.title.a};
text-decoration: none;
animation: animate 1.5s linear infinite;
}
@keyframes animate {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
a:hover,
a:focus,
a:active {
text-decoration: underline;
}
`
export default Title

View File

@ -1,11 +0,0 @@
import styled from 'styled-components'
const Main = styled.main`
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`
export default Main

View File

@ -1,8 +0,0 @@
import styled from "styled-components"
const IconDiv = styled.div`
padding-right: 5px;
padding-left: 5px;
`
export default IconDiv

View File

@ -1,9 +0,0 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import styled from "styled-components"
const Icon = styled(FontAwesomeIcon)`
padding-right: 5px;
padding-left: 5px;
`
export default Icon

View File

@ -1,71 +0,0 @@
import React, { useState } from 'react'
import { Backdrop, Box, Button, Fade, Link, Modal, TextField } from "@mui/material"
import { useCookies } from "react-cookie"
import api from '../../../api_utils'
import style from './style'
function CreateFolderModal({ open, setOpen, refresh }: Props) {
const [name, setName] = useState("")
const [cookies] = useCookies(["token"])
const handleChange: React.ChangeEventHandler<HTMLInputElement> = event => {
const value = event.target.value
setName(value)
}
// handle click "Enter (Return)"
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.keyCode === 13 || event.which === 13 || event.charCode === 13) {
handle()
}
}
const handle = () => {
setOpen(false)
const request = api.createDir(name, cookies.token)
request
.then(refresh)
}
return (
<Modal
open={open}
onClose={() => setOpen(false)}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<Box sx={style}>
<TextField
label="Folder"
placeholder="Folder"
margin="normal"
value={name}
onChange={handleChange}
onKeyPress={handleKeyPress}
/>
<Link>
<Button variant="outlined" onClick={handle}>
Create Folder
</Button>
</Link>
</Box>
</Fade>
</Modal>
)
}
export type Props = {
open: boolean,
setOpen: React.Dispatch<React.SetStateAction<boolean>>,
refresh: () => void,
}
export default CreateFolderModal

View File

@ -1,14 +0,0 @@
const style = {
position: 'absolute' as 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
color: 'primary.main',
border: '2px solid #fff',
boxShadow: 24,
p: 4
}
export default style

View File

@ -1,93 +0,0 @@
import React, { useState } from 'react'
import { Backdrop, Box, Button, Fade, Link, Modal } from "@mui/material"
import { useCookies } from "react-cookie"
import { toast } from 'react-toastify'
import api from '../../../api_utils'
import style from './style'
function UploadModal({ open, setOpen, path, refresh }: Props) {
const [file, setFile]: [FileList | null | undefined, React.Dispatch<React.SetStateAction<FileList | null | undefined>>] = useState()
const [cookies] = useCookies(["token"])
const onFileChange: React.ChangeEventHandler<HTMLInputElement> = event => {
setFile(event.target.files)
console.log(file)
}
const handleUpload = () => {
const formData = new FormData()
if (file == null || typeof file == "undefined") {
return
}
formData.append(
"file",
file[0]
)
const filePath = `${path}/${file[0].name}`
const request = api.upload(filePath, formData, cookies.token)
toast.promise(
request,
{
pending: 'Uploading file...',
success: {
delay: 500,
render() {
refresh()
setOpen(false)
return "File uploaded!"
}
},
error: {
delay: 500,
render(err) {
if (err.data.response?.data?.error_message) {
return err.data.response.data.error_message.toString()
} else {
return err.data.toString()
}
}
}
}
)
}
return (
<Modal
open={open}
onClose={() => setOpen(false)}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<Box sx={style}>
<input type="file" onChange={onFileChange} />
<Link>
<Button variant="outlined" onClick={handleUpload}>
Upload
</Button>
</Link>
</Box>
</Fade>
</Modal>
)
}
export type Props = {
open: boolean,
setOpen: React.Dispatch<React.SetStateAction<boolean>>,
path: string,
refresh: () => void,
}
export default UploadModal

View File

@ -1,37 +0,0 @@
import styled from "styled-components"
const Table = styled.table`
border: 1px solid;
width: 80vw;
border-collapse: collapse;
thead {
background-color: #01754b;
color: white;
}
tr,
td {
border: 1px solid #000;
padding: 8px;
}
td:first-child {
width: 50%;
}
td:last-child {
width: 20%;
}
a {
text-decoration: none;
}
a:hover {
cursor: pointer;
}
`
export default Table

View File

@ -1,8 +0,0 @@
export const links = {
github: "https://github.com/HomeDisk/cloud"
}
export default {
apiUrl: "/api",
links,
}

View File

@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -1,10 +0,0 @@
module.exports = {
async rewrites() {
return [
{
source: '/api/:slug*',
destination: 'http://127.0.0.1:8080/:slug*'
}
]
}
}

View File

@ -1,56 +0,0 @@
import { Button } from '@mui/material'
import Head from 'next/head'
import styled from 'styled-components'
const Title = styled.h1`
margin: 0;
line-height: 1.15;
font-size: 1.5rem;
text-align: center;
a {
color: ${({ theme }) => theme.pages.index.title.a};
text-decoration: none;
animation: animate 1.5s linear infinite;
}
@keyframes animate {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
a:hover,
a:focus,
a:active {
text-decoration: underline;
}
`
const StyledButton = styled(Button)`
margin-top: 1rem;
`
export default function NotFound() {
return (
<>
<Head>
<title>404 - HomeDisk</title>
</Head>
<Title>
404 | This page could not be found
</Title>
<StyledButton href="/">
Go to Home Page
</StyledButton>
</>
)
}

View File

@ -1,56 +0,0 @@
import { Button } from '@mui/material'
import Head from 'next/head'
import styled from 'styled-components'
const Title = styled.h1`
margin: 0;
line-height: 1.15;
font-size: 1.5rem;
text-align: center;
a {
color: ${({ theme }) => theme.pages.index.title.a};
text-decoration: none;
animation: animate 1.5s linear infinite;
}
@keyframes animate {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
a:hover,
a:focus,
a:active {
text-decoration: underline;
}
`
const StyledButton = styled(Button)`
margin-top: 1rem;
`
export default function NotFound() {
return (
<>
<Head>
<title>500 - HomeDisk</title>
</Head>
<Title>
500 | Server-side error occurred
</Title>
<StyledButton href="/">
Go to Home Page
</StyledButton>
</>
)
}

View File

@ -1,136 +0,0 @@
import { ThemeProvider as MuiThemeProvider, createTheme as muiCreateTheme, PaletteMode } from '@mui/material'
import { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import { ToastContainer } from 'react-toastify'
import { createGlobalStyle, ThemeProvider } from 'styled-components'
import Container from '../components/container'
import Footer from '../components/footer'
import Header from '../components/header'
import Main from '../components/main'
import "react-toastify/dist/ReactToastify.css"
const GlobalStyle = createGlobalStyle`
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
`
const lightTheme = {
colors: {
background: "#ffffff",
color: "#000000",
error: "#f85b5b"
},
pages: {
index: {
cards: {
signin: "#0a60cf",
register: "#a06800",
files: "#3d8011",
settings: "#75006f"
},
title: {
a: "#a109c0"
}
}
},
footer: {
borderTop: "#eaeaea"
}
}
const darkTheme = {
colors: {
background: "#131212",
color: "#ffffff",
error: "#f85b5b"
},
pages: {
index: {
cards: {
signin: "#0a60cf",
register: "#a06800",
files: "#54ad19",
settings: "#c90dbf"
},
title: {
a: "#a109c0"
}
}
},
footer: {
borderTop: "#161616"
}
}
export default function App({ Component, pageProps }) {
const [cookies, setCookies] = useCookies(["theme"])
const [theme, setTheme] = useState(lightTheme)
const [themeName, setThemeName]: [PaletteMode, any] = useState("light")
useEffect(() => {
if (!cookies.theme) setCookies("theme", "light")
if (cookies.theme == "dark"){
setTheme(darkTheme)
setThemeName("dark")
}
}, [setCookies, setTheme, setThemeName, cookies])
const toggleTheme = () => {
if (cookies.theme == "light") {
setTheme(darkTheme)
setThemeName("dark")
setCookies("theme", "dark")
}
if (cookies.theme == "dark") {
setTheme(lightTheme)
setThemeName("light")
setCookies("theme", "light")
}
}
const muiThene = muiCreateTheme({
palette: {
mode: themeName,
},
})
return (
<>
<GlobalStyle />
<ToastContainer theme={themeName} />
<MuiThemeProvider theme={muiThene}>
<ThemeProvider theme={theme}>
<Container>
<Header toggleTheme={toggleTheme} theme={themeName} />
<Main>
<Component {...pageProps} />
</Main>
<Footer />
</Container>
</ThemeProvider>
</MuiThemeProvider>
</>
)
}

View File

@ -1,32 +0,0 @@
import Document, { DocumentContext, DocumentInitialProps } from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: [
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>,
],
}
} finally {
sheet.seal()
}
}
}

View File

@ -1,80 +0,0 @@
import Head from 'next/head'
import { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import { Card, CardFiles, CardRegister, CardSettings, CardSignIn } from '../components/home/cards'
import Description from '../components/home/description'
import Grid from '../components/home/grid'
import Title from '../components/home/title'
import { links } from '../config'
export default function Home() {
const [cookies] = useCookies(["token"])
const [cards, setCards] = useState(<CardsNonLogged />)
useEffect(() => {
if (cookies.token) {
setCards(<CardsLogged />)
}
}, [setCards, cookies])
return (
<>
<Head>
<title>HomeDisk</title>
</Head>
<Title>
Welcome to <a href={links.github} target="_blank" rel="noreferrer">HomeDisk!</a>
</Title>
<Description>
Fast and lightweight local cloud for your data written in Rust
</Description>
<Grid>
{cards}
</Grid>
</>
)
}
function CardsNonLogged() {
return (
<>
<Card>
<CardSignIn href="/login">
<h2>Sign in &rarr;</h2>
<p>Log in to your account</p>
</CardSignIn>
</Card>
<Card>
<CardRegister href="/register">
<h2>Register &rarr;</h2>
<p>Register a new account</p>
</CardRegister>
</Card>
</>
)
}
function CardsLogged() {
return (
<>
<Card>
<CardFiles href="/user/files">
<h2>Files &rarr;</h2>
<p>View your files</p>
</CardFiles>
</Card>
<Card>
<CardSettings href="/user/settings">
<h2>Settings &rarr;</h2>
<p>Go to user settings</p>
</CardSettings>
</Card>
</>
)
}

View File

@ -1,97 +0,0 @@
import React from 'react'
import { TextField } from '@mui/material'
import Head from 'next/head'
import Router from 'next/router'
import { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import api from '../api_utils'
import Title from '../components/auth/title'
import ErrorComponent from '../components/auth/error'
import SubmitButton from '../components/auth/button'
export default function Login() {
const [cookies, setCookies] = useCookies(["token"])
useEffect(() => {
if (cookies.token) {
Router.push('/user/files')
}
})
const [error, setError] = useState("")
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const handleUsernameChange: React.ChangeEventHandler<HTMLInputElement> = event => {
const value = event.target.value
setUsername(value)
}
const handlePasswordChange: React.ChangeEventHandler<HTMLInputElement> = event => {
const value = event.target.value
setPassword(value)
}
// handle click "Enter (Return)"
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.keyCode === 13 || event.which === 13 || event.charCode === 13) {
handleLogin()
}
}
const handleLogin = () => {
const request = api.login(username, password)
request
.then(token => {
setCookies("token", token)
setError("")
})
.catch(err => setError(err.toString()))
}
return (
<>
<Head>
<title>Login - HomeDisk</title>
</Head>
<Title>
Sign in
</Title>
{error != "" && (
<ErrorComponent>{error}</ErrorComponent>
)}
<TextField
label="Username"
placeholder="Username"
margin="normal"
value={username}
onChange={handleUsernameChange}
onKeyPress={handleKeyPress}
/>
<TextField
label="Password"
placeholder="Password"
type="password"
margin="normal"
value={password}
onChange={handlePasswordChange}
onKeyPress={handleKeyPress}
/>
<SubmitButton
variant="contained"
size="large"
color="secondary"
onClick={handleLogin}
disabled={username == "" || password == ""}
>
Login
</SubmitButton>
</>
)
}

View File

@ -1,97 +0,0 @@
import React from 'react'
import { TextField } from '@mui/material'
import Head from 'next/head'
import Router from 'next/router'
import { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import api from '../api_utils'
import ErrorComponent from '../components/auth/error'
import Title from '../components/auth/title'
import SubmitButton from '../components/auth/button'
export default function Login() {
const [cookies, setCookies] = useCookies(["token"])
useEffect(() => {
if (cookies.token) {
Router.push('/user/files')
}
})
const [error, setError] = useState("")
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const handleUsernameChange: React.ChangeEventHandler<HTMLInputElement> = event => {
const value = event.target.value
setUsername(value)
}
const handlePasswordChange: React.ChangeEventHandler<HTMLInputElement> = event => {
const value = event.target.value
setPassword(value)
}
// handle click "Enter (Return)"
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.keyCode === 13 || event.which === 13 || event.charCode === 13) {
handleLogin()
}
}
const handleLogin = () => {
const request = api.register(username, password)
request
.then(token => {
setCookies("token", token)
setError("")
})
.catch(err => setError(err.toString()))
}
return (
<>
<Head>
<title>Register - HomeDisk</title>
</Head>
<Title>
Register
</Title>
{error != "" && (
<ErrorComponent>{error}</ErrorComponent>
)}
<TextField
label="Username"
placeholder="Username"
margin="normal"
value={username}
onChange={handleUsernameChange}
onKeyPress={handleKeyPress}
/>
<TextField
label="Password"
placeholder="Password"
type="password"
margin="normal"
value={password}
onChange={handlePasswordChange}
onKeyPress={handleKeyPress}
/>
<SubmitButton
variant="contained"
size="large"
color="secondary"
onClick={handleLogin}
disabled={username == "" || password == ""}
>
Register
</SubmitButton>
</>
)
}

View File

@ -1,161 +0,0 @@
import { faFile, faFolder } from "@fortawesome/free-solid-svg-icons"
import { CloudUpload, CreateNewFolder } from "@mui/icons-material"
import { IconButton, Link as MuiLink } from "@mui/material"
import Head from 'next/head'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { useCookies } from 'react-cookie'
import { resolve as pathResolve } from 'path'
import api from '../../api_utils'
import Icon from "../../components/other/icon"
import Table from "../../components/user/table"
import UploadModal from "../../components/user/modals/upload"
import CreateFolderModal from "../../components/user/modals/create-folder"
import IconDiv from "../../components/other/icon-div"
export default function Files() {
const [cookies] = useCookies(["token"])
const [path, setPath] = useState("")
const [files, setFiles] = useState([{ name: "", size: "", modified: "" }])
const [folders, setFolders] = useState([{ name: "", size: "", modified: "" }])
const refresh = (path: string) => {
api.list(path, cookies.token)
.then(data => {
setPath(path)
setFolders(data.dirs)
setFiles(data.files)
})
.catch(err => console.error(err))
}
const refreshFolder = () => refresh(path)
useEffect(() => {
const params = new URLSearchParams(window.location.search)
const path = params.get("path") || ""
api.list(path, cookies.token)
.then(data => {
setPath(path)
setFolders(data.dirs)
setFiles(data.files)
})
.catch(err => console.error(err))
}, [cookies])
// modals
const [uploadModal, setUploadModal] = useState(false)
const [createFolderModal, setCreateFolderModal] = useState(false)
return (
<>
<Head>
<title>Files - HomeDisk</title>
</Head>
<MuiLink sx={{display: "flex"}}>
<IconDiv>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="logo"
onClick={() => setUploadModal(true)}
>
<CloudUpload />
</IconButton>
</IconDiv>
<IconDiv>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="logo"
onClick={() => setCreateFolderModal(true)}
>
<CreateNewFolder />
</IconButton>
</IconDiv>
</MuiLink>
<UploadModal open={uploadModal} setOpen={setUploadModal} path={path} refresh={refreshFolder} />
<CreateFolderModal open={createFolderModal} setOpen={setCreateFolderModal} refresh={refreshFolder} />
<Table>
<thead>
<tr>
<td>Name</td>
<td>Size</td>
<td>Modified</td>
</tr>
</thead>
<tbody>
{path != "" && path != "/" && (
<tr>
<td>
<Link href={`?path=${pathResolve(path, '..')}`}>
<MuiLink onClick={() => refresh(pathResolve(path, '..'))}>
<Icon icon={faFolder} />
.. (go up)
</MuiLink>
</Link>
</td>
<td></td>
<td></td>
</tr>
)}
{folders.map((f, index) => <FolderComponent key={index} name={f.name} path={`${path}/${f.name}`} size={f.size} modified={f.modified} refresh={refresh} />)}
{files.map((f, index) => <FileComponent key={index} name={f.name} path={`${path}/${f.name}`} size={f.size} modified={f.modified} refresh={refresh} />)}
</tbody>
</Table>
</>
)
}
function FolderComponent({ name, path, size, modified, refresh }: Props) {
return (
<tr>
<td>
<Link href={`?path=${path}`}>
<MuiLink onClick={() => refresh(path)}>
<Icon icon={faFolder} />
{name.replace("/", "")}
</MuiLink>
</Link>
</td>
<td>{size}</td>
<td>{modified}</td>
</tr>
)
}
function FileComponent({ name, path, size, modified, refresh }: Props) {
return (
<tr>
<td>
<Link href={`?path=${path}`}>
<MuiLink onClick={() => refresh(path)}>
<Icon icon={faFile} />
{name.replace("/", "")}
</MuiLink>
</Link>
</td>
<td>{size}</td>
<td>{modified} ago</td>
</tr>
)
}
type Props = {
name: string,
path: string,
size: string,
modified: string,
// eslint-disable-next-line no-unused-vars
refresh: (path: string) => void
}

View File

@ -1,20 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

View File

@ -1,23 +0,0 @@
interface ThemeInterface {
colors: {
background: string;
color: string;
error: string;
};
pages: {
index: {
cards: {
signin: string;
register: string;
};
title: {
a: string;
};
};
};
footer: {
borderTop: string;
};
}
export default ThemeInterface