28 | {t('This route (the folder itself and the files inside) is password protected. ') +
29 | t('If you know the password, please enter it below.')}
30 |
60 | )
61 | }
62 |
63 | export default Auth
64 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "onedrive-vercel-index",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "format": "prettier 'components/**/*.tsx' 'config/*.js' 'pages/**/*.{ts,tsx}' '{types,utils}/**/*.ts' --write",
11 | "extract": "i18next"
12 | },
13 | "dependencies": {
14 | "@fortawesome/fontawesome-svg-core": "^1.2.35",
15 | "@fortawesome/free-brands-svg-icons": "^5.15.4",
16 | "@fortawesome/free-regular-svg-icons": "^5.15.3",
17 | "@fortawesome/free-solid-svg-icons": "^5.15.3",
18 | "@fortawesome/react-fontawesome": "^0.1.14",
19 | "@headlessui/react": "^1.4.0",
20 | "@tailwindcss/line-clamp": "^0.3.1",
21 | "awesome-debounce-promise": "^2.1.0",
22 | "axios": "^0.25.0",
23 | "cors": "^2.8.5",
24 | "crypto-js": "^4.1.1",
25 | "csstype": "^2.6.2",
26 | "dayjs": "^1.10.7",
27 | "emoji-regex": "^10.0.0",
28 | "ioredis": "^4.28.2",
29 | "jszip": "^3.7.1",
30 | "mpegts.js": "^1.6.10",
31 | "next": "^12.0.10",
32 | "next-i18next": "^10.2.0",
33 | "nextjs-progressbar": "^0.0.13",
34 | "plyr-react": "^3.2.1",
35 | "preview-office-docs": "^1.0.2",
36 | "react": "^17.0.2",
37 | "react-async-hook": "^4.0.0",
38 | "react-audio-player": "^0.17.0",
39 | "react-cookie": "^4.1.1",
40 | "react-copy-to-clipboard": "^5.0.3",
41 | "react-dom": "^17.0.2",
42 | "react-hot-toast": "^2.0.0",
43 | "react-hotkeys-hook": "^3.4.4",
44 | "react-markdown": "^8.0.0",
45 | "react-reader": "^0.20.4",
46 | "react-syntax-highlighter": "^15.4.5",
47 | "react-use-system-theme": "^1.1.1",
48 | "rehype-katex": "^6.0.2",
49 | "rehype-raw": "^6.0.0",
50 | "remark-gfm": "^3.0.1",
51 | "remark-math": "^5.1.1",
52 | "swr": "^1.2.0",
53 | "use-clipboard-copy": "^0.2.0",
54 | "use-constant": "^1.1.0"
55 | },
56 | "devDependencies": {
57 | "@types/cors": "^2.8.12",
58 | "@types/crypto-js": "^4.0.2",
59 | "@types/ioredis": "^4.28.5",
60 | "@types/react": "17.0.38",
61 | "@types/react-copy-to-clipboard": "^5.0.0",
62 | "@types/react-dom": "^17.0.8",
63 | "@types/react-pdf": "^5.0.4",
64 | "@types/react-syntax-highlighter": "^13.5.1",
65 | "autoprefixer": "^10.4.0",
66 | "eslint": "8.8.0",
67 | "eslint-config-next": "12.0.10",
68 | "eslint-config-prettier": "^8.3.0",
69 | "i18next-parser": "^5.4.0",
70 | "postcss": "^8.4.5",
71 | "prettier": "^2.5.1",
72 | "prettier-plugin-tailwindcss": "^0.1.4",
73 | "tailwindcss": "^3.0.18",
74 | "typescript": "4.5.5"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/config/api.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains the configuration for the API endpoints and tokens we use.
3 | *
4 | * - If you are a OneDrive International user, you would not have to change anything here.
5 | * - If you are not the admin of your OneDrive for Business account, you may need to define your own clientId/clientSecret,
6 | * check documentation for more details.
7 | * - If you are using a E5 Subscription OneDrive for Business account, the direct links of your files are not the same here.
8 | * In which case you would need to change directLinkRegex.
9 | */
10 | module.exports = {
11 | // The clientId and clientSecret are used to authenticate the user with Microsoft Graph API using OAuth. You would
12 | // not need to change anything here if you can authenticate with your personal Microsoft account with OneDrive International.
13 | clientId: 'd87bcc39-1750-4ca0-ad54-f8d0efbb2735',
14 | obfuscatedClientSecret: 'U2FsdGVkX1830zo3/pFDqaBCVBb37iLw3WnBDWGF9GIB2f4apzv0roemp8Y+iIxI3Ih5ecyukqELQEGzZlYiWg==',
15 |
16 | // The redirectUri is the URL that the user will be redirected to after they have authenticated with Microsoft Graph API.
17 | // Likewise, you would not need to change redirectUri if you are using your personal Microsoft account with OneDrive International.
18 | redirectUri: 'http://localhost',
19 |
20 | // These are the URLs of the OneDrive API endpoints. You would not need to change anything here if you are using OneDrive International
21 | // or E5 Subscription OneDrive for Business. You may need to change these if you are using OneDrive 世纪互联.
22 | authApi: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
23 | driveApi: 'https://graph.microsoft.com/v1.0/me/drive',
24 |
25 | // The scope we require are listed here, in most cases you would not need to change this as well.
26 | scope: 'user.read files.read.all offline_access',
27 |
28 | // The directLinkRegex is used to match the direct link of the file from the response of the API. We originally use this to prevent
29 | // unauthorised use of the proxied download feature - but that is disabled for now. So you can safely ignore this settings.
30 | directLinkRegex: 'public[.].*[.]files[.]1drv[.]com',
31 |
32 | // Cache-Control header, check Vercel documentation for more details. The default settings imply:
33 | // - max-age=0: no cache for your browser
34 | // - s-maxage=0: cache is fresh for 60 seconds on the edge, after which it becomes stale
35 | // - stale-while-revalidate: allow serving stale content while revalidating on the edge
36 | // https://vercel.com/docs/concepts/edge-network/caching
37 | cacheControlHeader: 'max-age=0, s-maxage=60, stale-while-revalidate',
38 | }
39 |
--------------------------------------------------------------------------------
/components/SwitchLang.tsx:
--------------------------------------------------------------------------------
1 | import { Fragment } from 'react'
2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3 | import { Menu, Transition } from '@headlessui/react'
4 |
5 | import { useRouter } from 'next/router'
6 | import Link from 'next/link'
7 | import { useCookies, withCookies } from 'react-cookie'
8 |
9 | // https://headlessui.dev/react/menu#integrating-with-next-js
10 | const CustomLink = ({ href, children, as, locale, ...props }): JSX.Element => {
11 | return (
12 |
13 | {children}
14 |
15 | )
16 | }
17 |
18 | const localeText = (locale: string): string => {
19 | switch (locale) {
20 | case 'en':
21 | return '🇬🇧 English'
22 | case 'zh-CN':
23 | return '🇨🇳 简体中文'
24 | default:
25 | return '🇬🇧 English'
26 | }
27 | }
28 |
29 | const SwitchLang = () => {
30 | const { locales, pathname, query, asPath } = useRouter()
31 |
32 | const [_, setCookie] = useCookies(['NEXT_LOCALE'])
33 |
34 | return (
35 |
36 |
70 |
71 | )
72 | }
73 |
74 | export default withCookies(SwitchLang)
75 |
--------------------------------------------------------------------------------
/utils/useLocalStorage.ts:
--------------------------------------------------------------------------------
1 | import { Dispatch, SetStateAction, useEffect, useState } from 'react'
2 |
3 | type SetValue = Dispatch>
4 |
5 | function useLocalStorage(key: string, initialValue: T): [T, SetValue] {
6 | // Get from local storage then
7 | // parse stored json or return initialValue
8 | const readValue = (): T => {
9 | // Prevent build error "window is undefined" but keep keep working
10 | if (typeof window === 'undefined') {
11 | return initialValue
12 | }
13 |
14 | try {
15 | const item = window.localStorage.getItem(key)
16 | return item ? (JSON.parse(item) as T) : initialValue
17 | } catch (error) {
18 | console.warn(`Error reading localStorage key “${key}”:`, error)
19 | return initialValue
20 | }
21 | }
22 |
23 | // State to store our value
24 | // Pass initial state function to useState so logic is only executed once
25 | const [storedValue, setStoredValue] = useState(readValue)
26 |
27 | // Return a wrapped version of useState's setter function that ...
28 | // ... persists the new value to localStorage.
29 | const setValue: SetValue = value => {
30 | // Prevent build error "window is undefined" but keeps working
31 | if (typeof window == 'undefined') {
32 | console.warn(`Tried setting localStorage key “${key}” even though environment is not a client`)
33 | }
34 |
35 | try {
36 | // Allow value to be a function so we have the same API as useState
37 | const newValue = value instanceof Function ? value(storedValue) : value
38 |
39 | // Save to local storage
40 | window.localStorage.setItem(key, JSON.stringify(newValue))
41 |
42 | // Save state
43 | setStoredValue(newValue)
44 |
45 | // We dispatch a custom event so every useLocalStorage hook are notified
46 | window.dispatchEvent(new Event('local-storage'))
47 | } catch (error) {
48 | console.warn(`Error setting localStorage key “${key}”:`, error)
49 | }
50 | }
51 |
52 | useEffect(() => {
53 | setStoredValue(readValue())
54 | // eslint-disable-next-line react-hooks/exhaustive-deps
55 | }, [])
56 |
57 | useEffect(() => {
58 | const handleStorageChange = () => {
59 | setStoredValue(readValue())
60 | }
61 |
62 | // this only works for other documents, not the current one
63 | window.addEventListener('storage', handleStorageChange)
64 |
65 | // this is a custom event, triggered in writeValueToLocalStorage
66 | window.addEventListener('local-storage', handleStorageChange)
67 |
68 | return () => {
69 | window.removeEventListener('storage', handleStorageChange)
70 | window.removeEventListener('local-storage', handleStorageChange)
71 | }
72 | // eslint-disable-next-line react-hooks/exhaustive-deps
73 | }, [])
74 |
75 | return [storedValue, setValue]
76 | }
77 |
78 | export default useLocalStorage
79 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 | import '../styles/markdown-github.css'
3 |
4 | import { library } from '@fortawesome/fontawesome-svg-core'
5 | import {
6 | faFileImage,
7 | faFilePdf,
8 | faFileWord,
9 | faFilePowerpoint,
10 | faFileExcel,
11 | faFileAudio,
12 | faFileVideo,
13 | faFileArchive,
14 | faFileCode,
15 | faFileAlt,
16 | faFile,
17 | faFolder,
18 | faCopy,
19 | faArrowAltCircleDown,
20 | faTrashAlt,
21 | faEnvelope,
22 | faFlag,
23 | faCheckCircle,
24 | } from '@fortawesome/free-regular-svg-icons'
25 | import {
26 | faSearch,
27 | faPen,
28 | faCheck,
29 | faPlus,
30 | faMinus,
31 | faCopy as faCopySolid,
32 | faAngleRight,
33 | faDownload,
34 | faMusic,
35 | faArrowLeft,
36 | faArrowRight,
37 | faFileDownload,
38 | faUndo,
39 | faBook,
40 | faKey,
41 | faSignOutAlt,
42 | faCloud,
43 | faChevronCircleDown,
44 | faChevronDown,
45 | faLink,
46 | faExternalLinkAlt,
47 | faExclamationCircle,
48 | faExclamationTriangle,
49 | faTh,
50 | faThLarge,
51 | faThList,
52 | faHome,
53 | faLanguage,
54 | } from '@fortawesome/free-solid-svg-icons'
55 | import * as Icons from '@fortawesome/free-brands-svg-icons'
56 |
57 | import type { AppProps } from 'next/app'
58 | import NextNProgress from 'nextjs-progressbar'
59 | import { appWithTranslation } from 'next-i18next'
60 |
61 | // import all brand icons with tree-shaking so all icons can be referenced in the app
62 | const iconList = Object.keys(Icons)
63 | .filter(k => k !== 'fab' && k !== 'prefix')
64 | .map(icon => Icons[icon])
65 |
66 | library.add(
67 | faFileImage,
68 | faFilePdf,
69 | faFileWord,
70 | faFilePowerpoint,
71 | faFileExcel,
72 | faFileAudio,
73 | faFileVideo,
74 | faFileArchive,
75 | faFileCode,
76 | faFileAlt,
77 | faFile,
78 | faFlag,
79 | faFolder,
80 | faMusic,
81 | faArrowLeft,
82 | faArrowRight,
83 | faAngleRight,
84 | faFileDownload,
85 | faCopy,
86 | faCopySolid,
87 | faPlus,
88 | faMinus,
89 | faDownload,
90 | faLink,
91 | faUndo,
92 | faBook,
93 | faArrowAltCircleDown,
94 | faKey,
95 | faTrashAlt,
96 | faSignOutAlt,
97 | faEnvelope,
98 | faCloud,
99 | faChevronCircleDown,
100 | faExternalLinkAlt,
101 | faExclamationCircle,
102 | faExclamationTriangle,
103 | faHome,
104 | faCheck,
105 | faCheckCircle,
106 | faSearch,
107 | faChevronDown,
108 | faTh,
109 | faThLarge,
110 | faThList,
111 | faLanguage,
112 | faPen,
113 | ...iconList
114 | )
115 |
116 | function MyApp({ Component, pageProps }: AppProps) {
117 | return (
118 | <>
119 |
120 |
121 | >
122 | )
123 | }
124 | export default appWithTranslation(MyApp)
125 |
--------------------------------------------------------------------------------
/components/previews/EPUBPreview.tsx:
--------------------------------------------------------------------------------
1 | import type { OdFileObject } from '../../types'
2 |
3 | import { FC, useEffect, useRef, useState } from 'react'
4 | import { ReactReader } from 'react-reader'
5 | import { useRouter } from 'next/router'
6 | import { useTranslation } from 'next-i18next'
7 |
8 | import Loading from '../Loading'
9 | import DownloadButtonGroup from '../DownloadBtnGtoup'
10 | import { DownloadBtnContainer } from './Containers'
11 | import { getStoredToken } from '../../utils/protectedRouteHandler'
12 |
13 | const EPUBPreview: FC<{ file: OdFileObject }> = ({ file }) => {
14 | const { asPath } = useRouter()
15 | const hashedToken = getStoredToken(asPath)
16 |
17 | const [epubContainerWidth, setEpubContainerWidth] = useState(400)
18 | const epubContainer = useRef(null)
19 |
20 | useEffect(() => {
21 | setEpubContainerWidth(epubContainer.current ? epubContainer.current.offsetWidth : 400)
22 | }, [])
23 |
24 | const [location, setLocation] = useState()
25 | const onLocationChange = (cfiStr: string) => setLocation(cfiStr)
26 |
27 | const { t } = useTranslation()
28 |
29 | // Fix for not valid epub files according to
30 | // https://github.com/gerhardsletten/react-reader/issues/33#issuecomment-673964947
31 | const fixEpub = rendition => {
32 | const spineGet = rendition.book.spine.get.bind(rendition.book.spine)
33 | rendition.book.spine.get = function (target: string) {
34 | const targetStr = target as string
35 | let t = spineGet(target)
36 | while (t == null && targetStr.startsWith('../')) {
37 | target = targetStr.substring(3)
38 | t = spineGet(target)
39 | }
40 | return t
41 | }
42 | }
43 |
44 | return (
45 |
76 | )
77 | }
78 |
79 | export default SwitchLayout
80 |
--------------------------------------------------------------------------------
/utils/getFileIcon.ts:
--------------------------------------------------------------------------------
1 | import type { IconPrefix, IconName } from '@fortawesome/fontawesome-common-types'
2 |
3 | const icons: { [key: string]: [IconPrefix, IconName] } = {
4 | image: ['far', 'file-image'],
5 | pdf: ['far', 'file-pdf'],
6 | word: ['far', 'file-word'],
7 | powerpoint: ['far', 'file-powerpoint'],
8 | excel: ['far', 'file-excel'],
9 | audio: ['far', 'file-audio'],
10 | video: ['far', 'file-video'],
11 | archive: ['far', 'file-archive'],
12 | code: ['far', 'file-code'],
13 | text: ['far', 'file-alt'],
14 | file: ['far', 'file'],
15 | markdown: ['fab', 'markdown'],
16 | book: ['fas', 'book'],
17 | link: ['fas', 'link'],
18 | }
19 |
20 | const extensions = {
21 | gif: icons.image,
22 | jpeg: icons.image,
23 | jpg: icons.image,
24 | png: icons.image,
25 | heic: icons.image,
26 | webp: icons.image,
27 |
28 | pdf: icons.pdf,
29 |
30 | doc: icons.word,
31 | docx: icons.word,
32 |
33 | ppt: icons.powerpoint,
34 | pptx: icons.powerpoint,
35 |
36 | xls: icons.excel,
37 | xlsx: icons.excel,
38 |
39 | aac: icons.audio,
40 | mp3: icons.audio,
41 | ogg: icons.audio,
42 | flac: icons.audio,
43 | oga: icons.audio,
44 | opus: icons.audio,
45 | m4a: icons.audio,
46 |
47 | avi: icons.video,
48 | flv: icons.video,
49 | mkv: icons.video,
50 | mp4: icons.video,
51 |
52 | '7z': icons.archive,
53 | bz2: icons.archive,
54 | xz: icons.archive,
55 | wim: icons.archive,
56 | gz: icons.archive,
57 | rar: icons.archive,
58 | tar: icons.archive,
59 | zip: icons.archive,
60 |
61 | c: icons.code,
62 | cpp: icons.code,
63 | js: icons.code,
64 | jsx: icons.code,
65 | java: icons.code,
66 | sh: icons.code,
67 | cs: icons.code,
68 | py: icons.code,
69 | css: icons.code,
70 | html: icons.code,
71 | ts: icons.code,
72 | tsx: icons.code,
73 | rs: icons.code,
74 | vue: icons.code,
75 | json: icons.code,
76 | yml: icons.code,
77 | yaml: icons.code,
78 | toml: icons.code,
79 |
80 | txt: icons.text,
81 | rtf: icons.text,
82 | vtt: icons.text,
83 | srt: icons.text,
84 | log: icons.text,
85 | diff: icons.text,
86 |
87 | md: icons.markdown,
88 |
89 | epub: icons.book,
90 | mobi: icons.book,
91 | azw3: icons.book,
92 |
93 | url: icons.link,
94 | }
95 |
96 | /**
97 | * To stop TypeScript complaining about indexing the object with a non-existent key
98 | * https://dev.to/mapleleaf/indexing-objects-in-typescript-1cgi
99 | *
100 | * @param obj Object with keys to index
101 | * @param key The index key
102 | * @returns Whether or not the key exists inside the object
103 | */
104 | export function hasKey(obj: O, key: PropertyKey): key is keyof O {
105 | return key in obj
106 | }
107 |
108 | export function getRawExtension(fileName: string): string {
109 | return fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2)
110 | }
111 | export function getExtension(fileName: string): string {
112 | return getRawExtension(fileName).toLowerCase()
113 | }
114 |
115 | export function getFileIcon(fileName: string, flags?: { video?: boolean }): [IconPrefix, IconName] {
116 | const extension = getExtension(fileName)
117 | let icon = hasKey(extensions, extension) ? extensions[extension] : icons.file
118 |
119 | // Files with '.ts' extensions may be TypeScript files or TS Video files, we check for the flag 'video'
120 | // to determine which icon to render for '.ts' files.
121 | if (extension === 'ts') {
122 | if (flags?.video) {
123 | icon = icons.video
124 | }
125 | }
126 |
127 | return icon
128 | }
129 |
--------------------------------------------------------------------------------
/config/site.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains the configuration used for customising the website, such as the folder to share,
3 | * the title, used Google fonts, site icons, contact info, etc.
4 | */
5 | module.exports = {
6 | // This is what we use to identify who you are when you are initialising the website for the first time.
7 | // Make sure this is exactly the same as the email address you use to sign into your Microsoft account.
8 | // You can also put this in your Vercel's environment variable 'NEXT_PUBLIC_USER_PRINCIPLE_NAME' if you worry about
9 | // your email being exposed in public.
10 | userPrincipalName: process.env.NEXT_PUBLIC_USER_PRINCIPLE_NAME || 'swl8023@outlook.com',
11 |
12 | // [OPTIONAL] This is the website icon to the left of the title inside the navigation bar. It should be placed under the
13 | // /public directory of your GitHub project (not your OneDrive folder!), and referenced here by its relative path to /public.
14 | icon: '/icons/128.png',
15 |
16 | // The name of your website. Present alongside your icon.
17 | title: "Spencer's OneDrive",
18 |
19 | // The folder that you are to share publicly with onedrive-vercel-index. Use '/' if you want to share your root folder.
20 | baseDirectory: '/',
21 |
22 | // [OPTIONAL] This represents the maximum number of items that one directory lists, pagination supported.
23 | // Do note that this is limited up to 200 items by the upstream OneDrive API.
24 | maxItems: 100,
25 |
26 | // [OPTIONAL] We use Google Fonts natively for font customisations.
27 | // You can check and generate the required links and names at https://fonts.google.com.
28 | // googleFontSans - the sans serif font used in onedrive-vercel-index.
29 | googleFontSans: 'Inter',
30 | // googleFontMono - the monospace font used in onedrive-vercel-index.
31 | googleFontMono: 'Fira Mono',
32 | // googleFontLinks - an array of links for referencing the google font assets.
33 | googleFontLinks: ['https://fonts.googleapis.com/css2?family=Fira+Mono&family=Inter:wght@400;500;700&display=swap'],
34 |
35 | // [OPTIONAL] The footer component of your website. You can write HTML here, but you need to escape double
36 | // quotes - changing " to \". You can write anything here, and if you like badges, generate some with https://shields.io
37 | footer:
38 | 'Powered by onedrive-vercel-index. Made with ❤ by SpencerWoo.',
39 |
40 | // [OPTIONAL] This is where you specify the folders that are password protected. It is an array of paths pointing to all
41 | // the directories in which you have .password set. Check the documentation for details.
42 | protectedRoutes: ['/🌞 Private folder/u-need-a-password', '/🥟 Some test files/Protected route'],
43 |
44 | // [OPTIONAL] Use "" here if you want to remove this email address from the nav bar.
45 | email: 'mailto:spencer.wushangbo@gmail.com',
46 |
47 | // [OPTIONAL] This is an array of names and links for setting your social information and links.
48 | // In the latest update, all brand icons inside font awesome is supported and the icon to render is based on the name
49 | // you provide. See the documentation for details.
50 | links: [
51 | {
52 | name: 'GitHub',
53 | link: 'https://github.com/spencerwooo/onedrive-vercel-index',
54 | },
55 | {
56 | name: 'Telegram',
57 | link: 'https://t.me/realSpencerWoo',
58 | },
59 | ],
60 |
61 | // This is a day.js-style datetime format string to format datetimes in the app. Ref to
62 | // https://day.js.org/docs/en/display/format for detailed specification. The default value is ISO 8601 full datetime
63 | // without timezone and replacing T with space.
64 | datetimeFormat: 'YYYY-MM-DD HH:mm:ss',
65 | }
66 |
--------------------------------------------------------------------------------
/components/previews/DefaultPreview.tsx:
--------------------------------------------------------------------------------
1 | import type { OdFileObject } from '../../types'
2 | import { FC } from 'react'
3 |
4 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
5 | import { useTranslation } from 'next-i18next'
6 |
7 | import { getFileIcon } from '../../utils/getFileIcon'
8 | import { formatModifiedDateTime, humanFileSize } from '../../utils/fileDetails'
9 |
10 | import DownloadButtonGroup from '../DownloadBtnGtoup'
11 | import { DownloadBtnContainer, PreviewContainer } from './Containers'
12 |
13 | const DefaultPreview: FC<{ file: OdFileObject }> = ({ file }) => {
14 | const { t } = useTranslation()
15 |
16 | return (
17 |
116 | {/* Using rehypeRaw to render HTML inside Markdown is potentially dangerous, use under safe environments. (#18) */}
117 |
122 | {content}
123 |
124 |
47 | {t('Welcome to your new onedrive-vercel-index 🎉')}
48 |
49 |
50 |
{t('Step 2/3: Get authorisation code')}
51 |
52 |
53 |
54 | If you are not the owner of this website,
55 | stop now, as continuing with this process may expose your personal files in OneDrive.
56 |
57 |
58 |
59 |
{
62 | window.open(oAuthUrl)
63 | }}
64 | >
65 |
66 |
67 |
68 |
69 | {oAuthUrl}
70 |
71 |
72 |
73 |
74 |
75 | The OAuth link for getting the authorisation code has been created. Click on the link above to get the{' '}
76 | authorisation code. Your browser will
77 | {/* eslint-disable-next-line react/no-unescaped-entities */}
78 | open a new tab to Microsoft's account login page. After logging in and authenticating with your
79 | Microsoft account, you will be redirected to a blank page on localhost. Paste{' '}
80 | the entire redirected URL down below.
81 |
82 |
106 | {authCode ?? {t('Waiting for code...')}}
107 |
108 |
109 |
110 | {authCode
111 | ? t('✅ You can now proceed onto the next step: requesting your access token and refresh token.')
112 | : t('❌ No valid code extracted.')}
113 |
114 |
115 |
116 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | )
142 | }
143 |
144 | export async function getServerSideProps({ locale }) {
145 | return {
146 | props: {
147 | ...(await serverSideTranslations(locale, ['common'])),
148 | },
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/public/locales/zh-CN/common.json:
--------------------------------------------------------------------------------
1 | {
2 | "- showing {{count}} page(s) ——other": "已显示 {{count}} 页",
3 | "{{count}} item(s)——other": "{{count}} 个项目",
4 | "<0>0> If you are not the owner of this website, stop now, as continuing with this process may expose your personal files in OneDrive.": "<0>0> 如果你不是这个网站的所有者,请立即停止操作,因为接下来的操作可能会暴露你的 OneDrive 私人文件。",
5 | "<0>0> If you have not specified a REDIS_URL inside your Vercel env variable, go initialise one at <3>Upstash3>. Docs: <6>Vercel Integration - Upstash6>.": "<0>0> 如果你还没有在 Vercel 中设置环境变量 REDIS_URL,你可以从 <3>Upstash3> 处获取一个来使用。文档:<6>Vercel 集成 - Upstash6>。",
6 | "<0>0> If you see anything missing or incorrect, you need to reconfigure <3>/config/api.config.js3> and redeploy this instance.": "<0>0> 如果你看到有遗漏或错误的项目,你需要重新编辑 <3>/config/api.config.js3> 并重新部署这个实例。",
7 | "✅ You can now proceed onto the next step: requesting your access token and refresh token.": "✅ 你现在可以进行下一步了:获取你的 access token 和 refresh token。",
8 | "❌ No valid code extracted.": "❌ 无法提取授权码。",
9 | "Acquired access_token: ": "获取 access_token",
10 | "Acquired refresh_token: ": "获取 refresh_token",
11 | "Actions": "操作",
12 | "Authorisation is required as no valid <2>access_token2> or <5>refresh_token5> is present on this deployed instance. Check the following configurations before proceeding with authorising onedrive-vercel-index with your own Microsoft account.": "本项目还没有设置有效的 <2>access_token2> 和 <5>refresh_token5>,需要进行授权。在继续对 onedrive-vercel-index 授权你的 Microsoft 帐号前,请检查一下下方的配置信息。",
13 | "Cancel": "取消",
14 | "Cannot preview {{path}}": "无法预览 {{path}}",
15 | "Change the raw file direct link to a URL ending with the extension of the file.": "将文件直链接更改为以文件扩展名结尾的 URL。",
16 | "Check out <2>Microsoft's official explanation2> on the error message.": "请查阅 <2>Microsoft 官方解释2> 以获取详细的错误信息。",
17 | "Clear all": "清除所有密钥",
18 | "Clear all tokens?": "清除所有密钥?",
19 | "Cleared all tokens": "已清除所有密钥",
20 | "clearing them means that you will need to re-enter the passwords again.": "清除它们意味着下次访问时你需要重新输入密钥。",
21 | "Copied direct link to clipboard.": "已复制直链到剪贴板。",
22 | "Copied folder permalink.": "已复制文件夹永久链接。",
23 | "Copied raw file permalink.": "已复制文件永久链接。",
24 | "Copy direct link": "复制文件直链",
25 | "Copy folder permalink": "复制文件夹永久链接",
26 | "Copy raw file permalink": "复制文件永久链接",
27 | "Copy the permalink to the file to the clipboard": "复制文件永久链接到剪贴板",
28 | "Customise direct link": "自定义文件直链",
29 | "Customise link": "自定义直链",
30 | "Customised": "自定义链接",
31 | "Customised and encoded": "URL 编码的自定义链接",
32 | "Default": "默认",
33 | "Do not pretend to be the site owner": "你不是网站所有者",
34 | "Don't worry, after storing them, onedrive-vercel-index will take care of token refreshes and updates after your site goes live.": "别担心,存储它们之后,onedrive-vercel-index 会在帮助你定时更新 token",
35 | "Download": "下载",
36 | "Download file": "下载此文件",
37 | "Download folder": "下载此文件夹",
38 | "Download selected files": "下载选定文件",
39 | "Download the file directly through OneDrive": "直接从 OneDrive 下载文件",
40 | "Downloading {{progress}}%": "已下载 {{progress}}%",
41 | "Downloading folder, refresh page to cancel": "下载文件夹中,刷新页面以取消",
42 | "Downloading selected files, refresh page to cancel": "下载选定文件中,刷新页面以取消",
43 | "Downloading selected files...": "下载选定文件中…",
44 | "Email": "电子邮件",
45 | "Enter Password": "输入密码",
46 | "Error storing the token": "存储 token 时出错",
47 | "Error validating identify, restart": "校验身份出错,需要重新开始",
48 | "Error: {{message}}": "错误:{{message}}",
49 | "Failed to download folder {{path}}: {{status}} {{message}} Skipped it to continue.": "下载文件夹 {{path}} 失败:{{status}} {{message}} 已忽略此错误并继续下载。",
50 | "Failed to download folder.": "下载文件夹失败。",
51 | "Failed to download selected files.": "下载选定文件失败。",
52 | "File is empty.": "文件为空。",
53 | "File size": "文件大小",
54 | "Filename": "文件名",
55 | "Final step, click the button below to store these tokens persistently before they expire after {{minutes}} minutes {{seconds}} seconds. ": "最后一步,在这些 tokens 于 {{minutes}} 分钟 {{seconds}} 秒后失效前,点击下方按钮以永久存储这些 tokens。",
56 | "Finished downloading folder.": "下载文件夹成功。",
57 | "Finished downloading selected files.": "下载选定文件成功。",
58 | "Get tokens": "获取 tokens",
59 | "Grid": "图格",
60 | "Hashes": "哈希值",
61 | "Home": "首页",
62 | "If you go back home and still see the welcome page telling you to re-authenticate, ": "如果你回到首页却仍然发现欢迎界面在提示你重新认证,",
63 | "If you know the password, please enter it below.": "如果你知晓密码,请在下面输入。",
64 | "Last modified": "最后修改时间",
65 | "Last Modified": "最后修改时间",
66 | "Last modified:": "最后修改时间:",
67 | "List": "列表",
68 | "Load more": "加载更多",
69 | "Loading ...": "加载中…",
70 | "Loading EPUB ...": "加载 EPUB 中…",
71 | "Loading file content...": "加载文件内容中…",
72 | "Loading FLV extension...": "加载 FLV 扩展中…",
73 | "Logout": "注销",
74 | "MIME type": "MIME 类型",
75 | "Name": "文件名",
76 | "No more files": "加载完毕",
77 | "Nothing here.": "无内容。",
78 | "OAuth Step 1 - {{title}}": "OAuth 第 1 步 - {{title}}",
79 | "OAuth Step 2 - {{title}}": "OAuth 第 2 步 - {{title}}",
80 | "OAuth Step 3 - {{title}}": "OAuth 第 3 步 - {{title}}",
81 | "of {{count}} file(s) -——loaded——other": "共 {{count}} 个文件",
82 | "of {{count}} file(s) -——loading——other": "共…个文件",
83 | "Oops, that's a <1>four-oh-four1>.": "Oops,这里是 <1>4041> 页面。",
84 | "Open URL": "打开 URL",
85 | "Open URL{{url}}": "打开 URL{{url}}",
86 | "Press <2>F122> and open devtools for more details, or seek help at <6>onedrive-vercel-index discussions6>.": "请按下 <2>F122> 来打开开发者工具窗口以获取详细信息,或是到 <6>onedrive-vercel-index 社区讨论6> 处寻求帮助。",
87 | "Proceed to OAuth": "继续进行 OAuth",
88 | "Requesting tokens": "正在获取 token",
89 | "Restart": "重新开始",
90 | "revisit home and do a hard refresh.": "重新访问首页并刷新浏览器",
91 | "Search ...": "搜索…",
92 | "Select all files": "选择所有文件",
93 | "Select file": "选择此文件",
94 | "Select files": "选择以下文件",
95 | "Size": "文件大小",
96 | "Step 1/3: Preparations": "步骤 1/3:准备",
97 | "Step 2/3: Get authorisation code": "步骤 2/3:获取授权码",
98 | "Step 3/3: Get access and refresh tokens": "步骤 3/3:获取 access token 和 refresh token",
99 | "Store tokens": "储存 tokens",
100 | "Stored! Going home...": "已存储!正在返回首页…",
101 | "Storing tokens": "正在存储 token…",
102 | "Success! The API returned what we needed.": "成功!需要的 token 已被返回。",
103 | "The authorisation code extracted is:": "提取出的授权码为:",
104 | "The OAuth link for getting the authorisation code has been created. Click on the link above to get the <2>authorisation code2>. Your browser willopen a new tab to Microsoft's account login page. After logging in and authenticating with your Microsoft account, you will be redirected to a blank page on localhost. Paste <6>the entire redirected URL6> down below.": "创建出的这个 OAuth 链接是用来获取授权码的。点击上方链接以获取所需的 <2>授权码2>。你的浏览器将在新的标签页打开 Microsoft 帐号登录页面。在登录并验证你的 Microsoft 帐号之后,你将被重定向到一个域名为 localhost 的空白页面。请将<6>完整的重定向后的 URL6> 整体复制粘贴到下方。",
105 | "These tokens are used to authenticate yourself into password protected folders, ": "这些密钥是用来验证你的身份以访问密钥保护下的文件夹的,",
106 | "These tokens may take a few seconds to populate after you click the button below. ": "当你点击下方按钮之后,这些 tokens 可能需要几秒钟来生成出现。",
107 | "This route (the folder itself and the files inside) is password protected. ": "此路由(此文件夹和其中的文件)是受密钥保护的。",
108 | "Unavailable": "无",
109 | "URL encoded": "URL 编码的链接",
110 | "Waiting for code...": "等待授权码…",
111 | "Weibo": "微博",
112 | "Welcome to your new onedrive-vercel-index 🎉": "欢迎来到你崭新的 onedrive-vercel-index 🎉",
113 | "What is this?": "这是什么?",
114 | "Where is the auth code? Did you follow step 2 you silly donut?": "授权码呢?你遵守了第 2 步吗?你这个傻瓜甜甜圈!o( ̄ヘ ̄o#)",
115 | "Whoops, looks like we got a problem: {{error}}.": "Whoops,看来我们遇到了一个问题:{{error}}"
116 | }
117 |
--------------------------------------------------------------------------------
/components/FolderListLayout.tsx:
--------------------------------------------------------------------------------
1 | import type { OdFolderChildren } from '../types'
2 |
3 | import Link from 'next/link'
4 | import { FC } from 'react'
5 | import { useClipboard } from 'use-clipboard-copy'
6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
7 | import { useTranslation } from 'next-i18next'
8 |
9 | import { getBaseUrl } from '../utils/getBaseUrl'
10 | import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails'
11 |
12 | import { Downloading, Checkbox, ChildIcon, ChildName } from './FileListing'
13 | import { getStoredToken } from '../utils/protectedRouteHandler'
14 |
15 | const FileListItem: FC<{ fileContent: OdFolderChildren }> = ({ fileContent: c }) => {
16 | return (
17 |
59 |
60 | Authorisation is required as no valid{' '}
61 | access_token or{' '}
62 | refresh_token{' '}
63 | is present on this deployed instance. Check the following configurations before proceeding with
64 | authorising onedrive-vercel-index with your own Microsoft account.
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | CLIENT_ID
74 |
75 |
76 | {apiConfig.clientId}
77 |
78 |
79 |
80 |
81 | CLIENT_SECRET*
82 |
83 |
84 | {apiConfig.obfuscatedClientSecret}
85 |
86 |
87 |
88 |
89 | REDIRECT_URI
90 |
91 |
92 | {apiConfig.redirectUri}
93 |
94 |
95 |
96 |
97 | Auth API URL
98 |
99 |
100 | {apiConfig.authApi}
101 |
102 |
103 |
104 |
105 | Drive API URL
106 |
107 |
108 | {apiConfig.driveApi}
109 |
110 |
111 |
112 |
113 | API Scope
114 |
115 |
116 | {apiConfig.scope}
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | If you see anything
126 | missing or incorrect, you need to reconfigure{' '}
127 | /config/api.config.js and redeploy this instance.
128 |
129 |
OneDrive public directory listing, powered by Vercel and Next.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## TL;DR
15 |
16 | Showcase, share, preview, and download files inside *your* OneDrive with onedrive-vercel-index -
17 |
18 | - Completely free to host 💸
19 | - Super fast ⚡ and responsive 💦
20 | - Takes less than 15 minutes to setup ⏱️
21 | - Highly customisable ⚒️
22 |
23 | 🍌 More importantly, we are pretty (●'◡'●)
24 |
25 | ## Quick start
26 |
27 | 🚀 Quick start: [Getting started](https://ovi.swo.moe/docs/getting-started).
28 |
29 | ## Discussion
30 |
31 | Please go to our [discussion forum](https://github.com/spencerwooo/onedrive-vercel-index/discussions) for general questions and FAQs, **issues are for bug reports and bug reports only.** Feature requests may or may not be ignored, as [I (@spencerwooo)](https://spencerwoo.com) am the only one maintaining the project, so **I only prioritise features that I use.**
32 |
33 | *If you happen to like this project, please give it a star!* :3
34 |
35 | *If you really, really like this project, please send money! -> [Sponsors 🤑 and donations 💰](https://ovi.swo.moe/sponsor)*
36 |
37 | ## Demo
38 |
39 | Live demo at [Spencer's OneDrive](https://drive.swo.moe).
40 |
41 | 
42 |
43 | ## Features
44 |
45 |
108 |
109 |
110 | )
111 | }
112 |
113 | function SearchResultItemLoadRemote({ result }: { result: OdSearchResult[number] }) {
114 | const { data, error }: SWRResponse = useSWR(`/api/item/?id=${result.id}`, fetcher)
115 |
116 | const { t } = useTranslation()
117 |
118 | if (error) {
119 | return
120 | }
121 | if (!data) {
122 | return (
123 |
129 | )
130 | }
131 |
132 | const driveItemPath = `${mapAbsolutePath(data.parentReference.path)}/${encodeURIComponent(data.name)}`
133 | return (
134 |
140 | )
141 | }
142 |
143 | function SearchResultItem({ result }: { result: OdSearchResult[number] }) {
144 | if (result.path === '') {
145 | // path is empty, which means we need to fetch the parentReference to get the path
146 | return
147 | } else {
148 | // path is not an empty string in the search result, such that we can directly render the component as is
149 | const driveItemPath = decodeURIComponent(result.path)
150 | return (
151 |
157 | )
158 | }
159 | }
160 |
161 | export default function SearchModal({
162 | searchOpen,
163 | setSearchOpen,
164 | }: {
165 | searchOpen: boolean
166 | setSearchOpen: Dispatch>
167 | }) {
168 | const { query, setQuery, results } = useDriveItemSearch()
169 |
170 | const { t } = useTranslation()
171 |
172 | const closeSearchBox = () => {
173 | setSearchOpen(false)
174 | setQuery('')
175 | }
176 |
177 | return (
178 |
179 |
247 |
248 | )
249 | }
250 |
--------------------------------------------------------------------------------
/public/locales/en/common.json:
--------------------------------------------------------------------------------
1 | {
2 | "- showing {{count}} page(s) ——one": "- showing {{count}} page ",
3 | "- showing {{count}} page(s) ——other": "- showing {{count}} pages ",
4 | "{{count}} item(s)——one": "{{count}} item",
5 | "{{count}} item(s)——other": "{{count}} items",
6 | "<0>0> If you are not the owner of this website, stop now, as continuing with this process may expose your personal files in OneDrive.": "<0>0> If you are not the owner of this website, stop now, as continuing with this process may expose your personal files in OneDrive.",
7 | "<0>0> If you have not specified a REDIS_URL inside your Vercel env variable, go initialise one at <3>Upstash3>. Docs: <6>Vercel Integration - Upstash6>.": "<0>0> If you have not specified a REDIS_URL inside your Vercel env variable, go initialise one at <3>Upstash3>. Docs: <6>Vercel Integration - Upstash6>.",
8 | "<0>0> If you see anything missing or incorrect, you need to reconfigure <3>/config/api.config.js3> and redeploy this instance.": "<0>0> If you see anything missing or incorrect, you need to reconfigure <3>/config/api.config.js3> and redeploy this instance.",
9 | "✅ You can now proceed onto the next step: requesting your access token and refresh token.": "✅ You can now proceed onto the next step: requesting your access token and refresh token.",
10 | "❌ No valid code extracted.": "❌ No valid code extracted.",
11 | "Acquired access_token: ": "Acquired access_token: ",
12 | "Acquired refresh_token: ": "Acquired refresh_token: ",
13 | "Actions": "Actions",
14 | "Authorisation is required as no valid <2>access_token2> or <5>refresh_token5> is present on this deployed instance. Check the following configurations before proceeding with authorising onedrive-vercel-index with your own Microsoft account.": "Authorisation is required as no valid <2>access_token2> or <5>refresh_token5> is present on this deployed instance. Check the following configurations before proceeding with authorising onedrive-vercel-index with your own Microsoft account.",
15 | "Cancel": "Cancel",
16 | "Cannot preview {{path}}": "Cannot preview {{path}}",
17 | "Change the raw file direct link to a URL ending with the extension of the file.": "Change the raw file direct link to a URL ending with the extension of the file.",
18 | "Check out <2>Microsoft's official explanation2> on the error message.": "Check out <2>Microsoft's official explanation2> on the error message.",
19 | "Clear all": "Clear all",
20 | "Clear all tokens?": "Clear all tokens?",
21 | "Cleared all tokens": "Cleared all tokens",
22 | "clearing them means that you will need to re-enter the passwords again.": "clearing them means that you will need to re-enter the passwords again.",
23 | "Copied direct link to clipboard.": "Copied direct link to clipboard.",
24 | "Copied folder permalink.": "Copied folder permalink.",
25 | "Copied raw file permalink.": "Copied raw file permalink.",
26 | "Copy direct link": "Copy direct link",
27 | "Copy folder permalink": "Copy folder permalink",
28 | "Copy raw file permalink": "Copy raw file permalink",
29 | "Copy the permalink to the file to the clipboard": "Copy the permalink to the file to the clipboard",
30 | "Customise direct link": "Customise direct link",
31 | "Customise link": "Customise link",
32 | "Customised": "Customised",
33 | "Customised and encoded": "Customised and encoded",
34 | "Default": "Default",
35 | "Do not pretend to be the site owner": "Do not pretend to be the site owner",
36 | "Don't worry, after storing them, onedrive-vercel-index will take care of token refreshes and updates after your site goes live.": "Don't worry, after storing them, onedrive-vercel-index will take care of token refreshes and updates after your site goes live.",
37 | "Download": "Download",
38 | "Download file": "Download file",
39 | "Download folder": "Download folder",
40 | "Download selected files": "Download selected files",
41 | "Download the file directly through OneDrive": "Download the file directly through OneDrive",
42 | "Downloading {{progress}}%": "Downloading {{progress}}%",
43 | "Downloading folder, refresh page to cancel": "Downloading folder, refresh page to cancel",
44 | "Downloading selected files, refresh page to cancel": "Downloading selected files, refresh page to cancel",
45 | "Downloading selected files...": "Downloading selected files...",
46 | "Email": "Email",
47 | "Enter Password": "Enter Password",
48 | "Error storing the token": "Error storing the token",
49 | "Error validating identify, restart": "Error validating identify, restart",
50 | "Error: {{message}}": "Error: {{message}}",
51 | "Failed to download folder {{path}}: {{status}} {{message}} Skipped it to continue.": "Failed to download folder {{path}}: {{status}} {{message}} Skipped it to continue.",
52 | "Failed to download folder.": "Failed to download folder.",
53 | "Failed to download selected files.": "Failed to download selected files.",
54 | "File is empty.": "File is empty.",
55 | "File size": "File size",
56 | "Filename": "Filename",
57 | "Final step, click the button below to store these tokens persistently before they expire after {{minutes}} minutes {{seconds}} seconds. ": "Final step, click the button below to store these tokens persistently before they expire after {{minutes}} minutes {{seconds}} seconds. ",
58 | "Finished downloading folder.": "Finished downloading folder.",
59 | "Finished downloading selected files.": "Finished downloading selected files.",
60 | "Get tokens": "Get tokens",
61 | "Grid": "Grid",
62 | "Hashes": "Hashes",
63 | "Home": "Home",
64 | "If you go back home and still see the welcome page telling you to re-authenticate, ": "If you go back home and still see the welcome page telling you to re-authenticate, ",
65 | "If you know the password, please enter it below.": "If you know the password, please enter it below.",
66 | "Last modified": "Last modified",
67 | "Last Modified": "Last Modified",
68 | "Last modified:": "Last modified:",
69 | "List": "List",
70 | "Load more": "Load more",
71 | "Loading ...": "Loading ...",
72 | "Loading EPUB ...": "Loading EPUB ...",
73 | "Loading file content...": "Loading file content...",
74 | "Loading FLV extension...": "Loading FLV extension...",
75 | "Logout": "Logout",
76 | "MIME type": "MIME type",
77 | "Name": "Name",
78 | "No more files": "No more files",
79 | "Nothing here.": "Nothing here.",
80 | "OAuth Step 1 - {{title}}": "OAuth Step 1 - {{title}}",
81 | "OAuth Step 2 - {{title}}": "OAuth Step 2 - {{title}}",
82 | "OAuth Step 3 - {{title}}": "OAuth Step 3 - {{title}}",
83 | "of {{count}} file(s) -——loaded——one": "of {{count}} file -",
84 | "of {{count}} file(s) -——loaded——other": "of {{count}} files -",
85 | "of {{count}} file(s) -——loading——one": "of ... file(s) -",
86 | "of {{count}} file(s) -——loading——other": "of ... file(s) -",
87 | "Oops, that's a <1>four-oh-four1>.": "Oops, that's a <1>four-oh-four1>.",
88 | "Open URL": "Open URL",
89 | "Open URL{{url}}": "Open URL{{url}}",
90 | "Press <2>F122> and open devtools for more details, or seek help at <6>onedrive-vercel-index discussions6>.": "Press <2>F122> and open devtools for more details, or seek help at <6>onedrive-vercel-index discussions6>.",
91 | "Proceed to OAuth": "Proceed to OAuth",
92 | "Requesting tokens": "Requesting tokens",
93 | "Restart": "Restart",
94 | "revisit home and do a hard refresh.": "revisit home and do a hard refresh.",
95 | "Search ...": "Search ...",
96 | "Select all files": "Select all files",
97 | "Select file": "Select file",
98 | "Select files": "Select files",
99 | "Size": "Size",
100 | "Step 1/3: Preparations": "Step 1/3: Preparations",
101 | "Step 2/3: Get authorisation code": "Step 2/3: Get authorisation code",
102 | "Step 3/3: Get access and refresh tokens": "Step 3/3: Get access and refresh tokens",
103 | "Store tokens": "Store tokens",
104 | "Stored! Going home...": "Stored! Going home...",
105 | "Storing tokens": "Storing tokens",
106 | "Success! The API returned what we needed.": "Success! The API returned what we needed.",
107 | "The authorisation code extracted is:": "The authorisation code extracted is:",
108 | "The OAuth link for getting the authorisation code has been created. Click on the link above to get the <2>authorisation code2>. Your browser willopen a new tab to Microsoft's account login page. After logging in and authenticating with your Microsoft account, you will be redirected to a blank page on localhost. Paste <6>the entire redirected URL6> down below.": "The OAuth link for getting the authorisation code has been created. Click on the link above to get the <2>authorisation code2>. Your browser willopen a new tab to Microsoft's account login page. After logging in and authenticating with your Microsoft account, you will be redirected to a blank page on localhost. Paste <6>the entire redirected URL6> down below.",
109 | "These tokens are used to authenticate yourself into password protected folders, ": "These tokens are used to authenticate yourself into password protected folders, ",
110 | "These tokens may take a few seconds to populate after you click the button below. ": "These tokens may take a few seconds to populate after you click the button below. ",
111 | "This route (the folder itself and the files inside) is password protected. ": "This route (the folder itself and the files inside) is password protected. ",
112 | "Unavailable": "Unavailable",
113 | "URL encoded": "URL encoded",
114 | "Waiting for code...": "Waiting for code...",
115 | "Weibo": "Weibo",
116 | "Welcome to your new onedrive-vercel-index 🎉": "Welcome to your new onedrive-vercel-index 🎉",
117 | "What is this?": "What is this?",
118 | "Where is the auth code? Did you follow step 2 you silly donut?": "Where is the auth code? Did you follow step 2 you silly donut?",
119 | "Whoops, looks like we got a problem: {{error}}.": "Whoops, looks like we got a problem: {{error}}."
120 | }
121 |
--------------------------------------------------------------------------------
/pages/onedrive-vercel-index-oauth/step-3.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import Image from 'next/image'
3 | import { useRouter } from 'next/router'
4 | import { useEffect, useState } from 'react'
5 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
6 | import { useTranslation, Trans } from 'next-i18next'
7 | import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
8 |
9 | import siteConfig from '../../config/site.config'
10 | import Navbar from '../../components/Navbar'
11 | import Footer from '../../components/Footer'
12 |
13 | import { getAuthPersonInfo, requestTokenWithAuthCode, sendTokenToServer } from '../../utils/oAuthHandler'
14 | import { LoadingIcon } from '../../components/Loading'
15 |
16 | export default function OAuthStep3({ accessToken, expiryTime, refreshToken, error, description, errorUri }) {
17 | const router = useRouter()
18 | const [expiryTimeLeft, setExpiryTimeLeft] = useState(expiryTime)
19 |
20 | const { t } = useTranslation()
21 |
22 | useEffect(() => {
23 | if (!expiryTimeLeft) return
24 |
25 | const intervalId = setInterval(() => {
26 | setExpiryTimeLeft(expiryTimeLeft - 1)
27 | }, 1000)
28 |
29 | return () => clearInterval(intervalId)
30 | }, [expiryTimeLeft])
31 |
32 | const [buttonContent, setButtonContent] = useState(
33 |
45 | )
46 |
47 | // verify identity of the authenticated user with the Microsoft Graph API
48 | const { data, status } = await getAuthPersonInfo(accessToken)
49 | if (status !== 200) {
50 | setButtonError(true)
51 | setButtonContent(
52 |
185 | {' '}
186 | {t('These tokens may take a few seconds to populate after you click the button below. ') +
187 | t('If you go back home and still see the welcome page telling you to re-authenticate, ') +
188 | t('revisit home and do a hard refresh.')}
189 |
190 |
191 | {t(
192 | 'Final step, click the button below to store these tokens persistently before they expire after {{minutes}} minutes {{seconds}} seconds. ',
193 | {
194 | minutes: Math.floor(expiryTimeLeft / 60),
195 | seconds: expiryTimeLeft - Math.floor(expiryTimeLeft / 60) * 60,
196 | }
197 | ) +
198 | t(
199 | "Don't worry, after storing them, onedrive-vercel-index will take care of token refreshes and updates after your site goes live."
200 | )}
201 |