Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #451 from mgilangjanuar/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
birdup000 authored Apr 7, 2023
2 parents 3ac21be + 4ee54e1 commit 5a13ca2
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 87 deletions.
41 changes: 22 additions & 19 deletions api/src/api/v1/Files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,17 +307,19 @@ export class Files {
const files = await prisma.files.findMany({
where: {
AND: [
{
name: {
startsWith: body.name.replace(/\.part0*\d+$/, '')
}
},
{
user_id: req.user?.id
},
{
parent_id: source.parent_id
},
{ name: source.name.endsWith('.part001') ? { startsWith: source.name.replace(/\.part0*\d+$/, '.part') } : source.name },
{ user_id: req.user?.id },
{ parent_id: source.parent_id },
]
}
})

const countExists = await prisma.files.count({
where: {
AND: [
{ name: source.name.endsWith('.part001') ? { startsWith: source.name.replace(/\.part0*\d+$/, ''), endsWith: '.part001' } : { startsWith: source.name } },
{ user_id: req.user?.id },
{ parent_id: body.parent_id }
]
}
})
Expand All @@ -328,8 +330,8 @@ export class Files {
const { forward_info: forwardInfo, message_id: messageId, mime_type: mimeType } = file
let peerFrom: Api.InputPeerChannel | Api.InputPeerUser | Api.InputPeerChat
let peerTo: Api.InputPeerChannel | Api.InputPeerUser | Api.InputPeerChat
const [type, peerId, _id, accessHash] = forwardInfo?.split('/') ?? []
if (forwardInfo && forwardInfo.match(/^channel\//gi)) {
const [type, peerId, _id, accessHash] = forwardInfo?.split('/') ?? []
if (type === 'channel') {
peerFrom = new Api.InputPeerChannel({
channelId: bigInt(peerId),
Expand All @@ -343,8 +345,8 @@ export class Files {
chatId: bigInt(peerId) })
}
}
const [type, peerId, _, accessHash] = ((req.user.settings as Prisma.JsonObject).saved_location as string).split('/')
if ((req.user.settings as Prisma.JsonObject)?.saved_location) {
const [type, peerId, _, accessHash] = ((req.user.settings as Prisma.JsonObject).saved_location as string).split('/')
if (type === 'channel') {
peerTo = new Api.InputPeerChannel({
channelId: bigInt(peerId),
Expand All @@ -368,7 +370,7 @@ export class Files {
dropAuthor: true
})) as any

const newForwardInfo = forwardInfo ? `${type}/${peerId}/${chat.updates[0].id.toString()}/${accessHash}` : null
const newForwardInfo = peerTo ? `${type}/${peerId}/${chat.updates[0].id.toString()}/${accessHash}` : null
const message = {
size: Number(file.size),
message_id: chat.updates[0].id.toString(),
Expand All @@ -380,7 +382,7 @@ export class Files {
const response = await prisma.files.create({
data: {
...body,
name: files.length == 1 ? body.name : body.name.replace(/\.part0*\d+$/, '')+`.part${String(countFiles + 1).padStart(3, '0')}`,
name: files.length == 1 ? body.name + `${countExists ? ` (${countExists})` : ''}` : body.name.replace(/\.part0*\d+$/, '')+`${countExists ? ` (${countExists})` : ''}`+`.part${String(countFiles + 1).padStart(3, '0')}`,
...message
}
})
Expand Down Expand Up @@ -1065,15 +1067,16 @@ export class Files {

const getSizes = ({ size, sizes }) => sizes ? sizes.pop() : size
const size = file.media.photo ? getSizes(file.media.photo.sizes.pop()) : file.media.document?.size
let type = file.media.photo || mimeType.match(/^image/gi) ? 'image' : null
let type = file.media.photo
if (file.media.document?.mimeType.match(/^video/gi) || name.match(/\.mp4$/gi) || name.match(/\.mkv$/gi) || name.match(/\.mov$/gi)) {
type = 'video'
} else if (file.media.document?.mimeType.match(/pdf$/gi) || name.match(/\.doc$/gi) || name.match(/\.docx$/gi) || name.match(/\.xls$/gi) || name.match(/\.xlsx$/gi)) {
type = 'document'
} else if (file.media.document?.mimeType.match(/audio$/gi) || name.match(/\.mp3$/gi) || name.match(/\.ogg$/gi)) {
type = 'audio'
} else if (file.media.document?.mimeType.match(/^image/gi) || name.match(/\.jpg$/gi) || name.match(/\.jpeg$/gi) || name.match(/\.png$/gi) || name.match(/\.gif$/gi)) {
type = 'image'
}

return {
name,
message_id: file.id.toString(),
Expand Down Expand Up @@ -1200,7 +1203,7 @@ export class Files {
const end = ranges[1] ? ranges[1] : totalFileSize.toJSNumber() - 1

const readStream = createReadStream(filename(), { start, end })
res.writeHead(206, {
res.writeHead(200, {
'Cache-Control': 'public, max-age=604800',
'ETag': Buffer.from(`${files[0].id}:${files[0].message_id}`).toString('base64'),
'Content-Range': `bytes ${start}-${end}/${totalFileSize}`,
Expand All @@ -1211,7 +1214,7 @@ export class Files {
})
readStream.pipe(res)
} else {
res.writeHead(206, {
res.writeHead(200, {
'Cache-Control': 'public, max-age=604800',
'ETag': Buffer.from(`${files[0].id}:${files[0].message_id}`).toString('base64'),
'Content-Range': `bytes */${totalFileSize}`,
Expand Down
13 changes: 9 additions & 4 deletions docs/docs/Installation/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ If it's succeed you don't need to follow the steps below.
npm i -g yarn
```

- Define all api variables in `./api/.env`, you can copy from `./api/.env.example`

- Define all
variables in `./api/.env`, you can copy from `./api/.env.example`


```shell
cp ./api/.env.example ./api/.env
Expand Down Expand Up @@ -124,7 +127,10 @@ yarn workspaces run build
## Run:

```shell

yarn api prisma migrate deploy


cd api && node dist/index.js
```

Expand All @@ -139,9 +145,8 @@ git pull origin main # or, staging for the latest updates

yarn install # install
yarn workspaces run build # build

yarn api prisma migrate deploy
yarn api prisma migrate deploy
cd api && node dist/index.js # run
```

Next, you can deploy TeleDrive with [Vercel](/docs/deployment/vercel) or [PM2](/docs/deployment/pm2).
Next, you can deploy TeleDrive with [Vercel](/docs/deployment/vercel) or [PM2](/docs/deployment/pm2).
32 changes: 32 additions & 0 deletions install.caprover.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash

set -e

echo "Node Version: $(node -v)"
echo "Yarn Version: $(yarn -v)"

if [ ! -f docker/.env ]
then
echo "Generating docker/.env file..."

ENV="develop"

echo "Preparing your keys from https://my.telegram.org/"
read -p "Enter your TG_API_ID: " TG_API_ID
read -p "Enter your TG_API_HASH: " TG_API_HASH

echo "ENV=$ENV" > docker/.env
echo "TG_API_ID=$TG_API_ID" >> docker/.env
echo "TG_API_HASH=$TG_API_HASH" >> docker/.env
fi

git reset --hard
git clean -f
git pull origin main

export $(cat docker/.env | xargs)

echo
echo "Build and deploy to CapRover..."
docker build --build-arg REACT_APP_TG_API_ID=$TG_API_ID --build-arg REACT_APP_TG_API_HASH=$TG_API_HASH -t myapp .
caprover deploy --appName myapp --imageName myapp
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
{
"name": "teledrive",
"version": "2.5.2",
"version": "2.5.4",
"repository": "git@github.com:mgilangjanuar/teledrive.git",
"author": "M Gilang Januar <mgilangjanuar@teledriveapp.com>",
"license": "MIT",
"private": true,
"engines": {
"node": "16.14.0"
},

"scripts": {
"web": "yarn workspace web",
"server": "yarn workspace api",
Expand Down
18 changes: 13 additions & 5 deletions web/src/pages/dashboard/components/Rename.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,40 @@ const Rename: React.FC<Props> = ({

const renameFile = async () => {
setLoadingRename(true)
const { name } = formRename.getFieldsValue()
const name = String(formRename.getFieldsValue().name)
try {
const { data: exists } = await req.get('/files', { params: { parent_id: fileRename.parent_id, name: name } })
if (/\.part0*\d*$/.test(name))
throw { status: 400, body: { error: 'The file name cannot end with ".part", even if followed by digits!' } }
if (/\(\d+\).+/.test(name))
throw { status: 400, body: { error: 'The file name cannot contain text after parentheses with digits inside!' } }
if (exists.length > 0)
throw { status: 400, body: { error: `A file/folder named "${name}" already exists!` } }

const { data: result } = await req.patch(`/files/${fileRename?.id}`, {
file: { name }
})
notification.success({
message: 'Success',
description: `${name} renamed successfully!`
description: `${fileRename?.name.replace(/\.part0*\d+$/, '')} renamed successfully!`
})
dataSource?.[1](dataSource?.[0].map((datum: any) => datum.id === result.file.id ? { ...datum, name } : datum))
setFileRename(undefined)
setLoadingRename(false)
formRename.resetFields()
onFinish?.()
} catch (error) {
} catch (error: any) {
setLoadingRename(false)
return notification.error({
message: 'Error',
description: 'Failed to rename a file. Please try again!'
description: error?.body?.error || 'Failed to rename a file. Please try again!'
})
}
}

return <Modal visible={fileRename}
onCancel={() => setFileRename(undefined)}
okText="Add"
okText="Rename"
title={<Typography.Text ellipsis>Rename {fileRename?.name.replace(/\.part0*\d+$/, '')}</Typography.Text>}
onOk={() => formRename.submit()}
cancelButtonProps={{ shape: 'round' }}
Expand Down
8 changes: 8 additions & 0 deletions web/src/pages/dashboard/components/Upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ const Upload: React.FC<Props> = ({ dataFileList: [fileList, setFileList], parent
let deleted = false

try {
const { data: exists } = await req.get('/files', { params: { parent_id: parent?.id, name: file.name } })
if (/\.part0*\d*$/.test(file.name))
throw { status: 400, body: { error: 'The file name cannot end with ".part", even if followed by digits!' } }
if (/\(\d+\).+/.test(file.name))
throw { status: 400, body: { error: 'The file name cannot contain text after parentheses with digits inside!' } }
if (exists.length > 0)
throw { status: 400, body: { error: `A file/folder named "${file.name}" already exists!` } }

while (filesWantToUpload.current?.findIndex(f => f.uid === file.uid) !== 0) {
await new Promise(res => setTimeout(res, 1000))
}
Expand Down
3 changes: 1 addition & 2 deletions web/src/pages/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,7 @@ const Dashboard: React.FC<PageProps & { me?: any, errorMe?: any }> = ({ match })
const name = `Link of ${row.name}`
await req.post('/files/addFolder', { file: { ...row, name, link_id: row.id, parent_id: p?.link_id || p?.id, id: undefined } })
} else {
const name = data?.find(datum => datum.name === row.name) ? `Copy of ${row.name}` : row.name
await req.post('/files/cloneFile', { file: { ...row, name, parent_id: p?.link_id || p?.id, id: undefined } })
await req.post('/files/cloneFile', { file: { ...row, name: row.name, parent_id: p?.link_id || p?.id, id: undefined } })
}
}))
} else if ((act || action) === 'cut') {
Expand Down
105 changes: 49 additions & 56 deletions web/src/utils/Download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,70 @@ import streamSaver from 'streamsaver'
import { Api } from 'telegram'
import { req } from './Fetcher'
import { telegramClient } from './Telegram'

async function downloadFile(client, file) {
let chat
if (file.forward_info && file.forward_info.match(/^channel\//gi)) {
const [type, peerId, id, accessHash] = file.forward_info.split('/')
let peer: Api.InputPeerChannel | Api.InputPeerUser | Api.InputPeerChat
if (type === 'channel') {
peer = new Api.InputPeerChannel({
channelId: BigInt(peerId) as any,
accessHash: BigInt(accessHash as string) as any,
})
chat = await client.invoke(
new Api.channels.GetMessages({
channel: peer,
id: [new Api.InputMessageID({ id: Number(id) })],
})
)
}
} else {
chat = await client.invoke(
new Api.messages.GetMessages({
id: [new Api.InputMessageID({ id: Number(file.message_id) })],
})
)
}
return client.downloadMedia(chat['messages'][0].media, {
outputFile: {
write: (chunk: Buffer) => {
return true
},
},
})
}
export async function download(id: string): Promise<ReadableStream> {
const { data: response } = await req.get(`/files/${id}`, { params: { raw: 1, as_array: 1 } })
let cancel = false
const { data: response } = await req.get(`/files/${id}`, {
params: { raw: 1, as_array: 1 },
})
const client = await telegramClient.connect()
const filesToDownload = response.files
const readableStream = new ReadableStream({
start(_controller: ReadableStreamDefaultController) {
},
async pull(controller: ReadableStreamDefaultController) {
let countFiles = 1
console.log('start downloading:', response.files)
for (const file of response.files) {
let chat: any
if (file.forward_info && file.forward_info.match(/^channel\//gi)) {
const [type, peerId, id, accessHash] = file.forward_info.split('/')
let peer: Api.InputPeerChannel | Api.InputPeerUser | Api.InputPeerChat
if (type === 'channel') {
peer = new Api.InputPeerChannel({
channelId: BigInt(peerId) as any,
accessHash: BigInt(accessHash as string) as any
})
chat = await client.invoke(new Api.channels.GetMessages({
channel: peer,
id: [new Api.InputMessageID({ id: Number(id) })]
}))
}
} else {
chat = await client.invoke(new Api.messages.GetMessages({
id: [new Api.InputMessageID({ id: Number(file.message_id) })]
}))
}
const getData = async () => await client.downloadMedia(chat['messages'][0].media, {
outputFile: {
write: (chunk: Buffer) => {
if (cancel) return false
return controller.enqueue(chunk)
},
close: () => {
if (countFiles++ >= Number(response.files.length)) controller.close()
}
},
progressCallback: (received, total) => {
console.log('progress: ', (Number(received) / Number(total) * 100).toFixed(2), '%')
}
})
async pull(controller) {
for (const [index, file] of filesToDownload.entries()) {
try {
await getData()
const data = await downloadFile(client, file)
controller.enqueue(data)
if (index === filesToDownload.length - 1) {
controller.close()
}
} catch (error) {
console.log(error)
console.error(error)
}
}
},
cancel() {
cancel = true
}
}, {
size(chunk: any) {
return chunk.length
}
})
return readableStream
}

export const directDownload = async (id: string, name: string): Promise<void> => {
const fileStream = streamSaver.createWriteStream(name)
const writer = fileStream.getWriter()
const reader = (await download(id)).getReader()
const pump = () => reader.read().then(({ value, done }) => {
const pump = async () => {
const { value, done } = await reader.read()
if (done) return writer.close()
writer.write(value)
return writer.ready.then(pump)
})
await writer.write(value)
await pump()
}
await pump()
}

0 comments on commit 5a13ca2

Please sign in to comment.