Skip to content

Commit

Permalink
Merge branch 'trunk' into fix/devcontainer
Browse files Browse the repository at this point in the history
  • Loading branch information
TacticalTechJay authored Sep 25, 2024
2 parents d5f6938 + 145b1ca commit ed0af04
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 34 deletions.
16 changes: 12 additions & 4 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ on:
workflow_dispatch:

jobs:
push_to_ghcr:
name: Push Release Image to GitHub Packages
push:
name: Push Release Image
runs-on: ubuntu-latest
steps:
- name: Check out the repo
Expand All @@ -32,20 +32,28 @@ jobs:
id: buildx
uses: docker/setup-buildx-action@v2

- name: Login to Github Packages
- name: Login to GitHub Packages
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build Docker Image
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and Push Docker Image
uses: docker/build-push-action@v3
with:
push: true
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/diced/zipline:latest
ghcr.io/diced/zipline:${{ steps.version.outputs.zipline_version }}
${{ secrets.DOCKERHUB_USERNAME }}/zipline:latest
${{ secrets.DOCKERHUB_USERNAME }}/zipline:${{ steps.version.outputs.zipline_version }}
cache-from: type=gha
cache-to: type=gha,mode=max
14 changes: 11 additions & 3 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ on:
workflow_dispatch:

jobs:
push_to_ghcr:
name: Push Image to GitHub Packages
push:
name: Push Image
runs-on: ubuntu-latest
steps:
- name: Check out the repo
Expand All @@ -38,13 +38,21 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build Docker Image
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and Push Docker Image
uses: docker/build-push-action@v3
with:
push: true
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/diced/zipline:trunk
ghcr.io/diced/zipline:trunk-${{ steps.version.outputs.zipline_version }}
${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk
${{ secrets.DOCKERHUB_USERNAME }}/zipline:trunk-${{ steps.version.outputs.zipline_version }}
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zipline",
"version": "3.7.9",
"version": "3.7.10",
"license": "MIT",
"scripts": {
"dev": "npm-run-all build:server dev:run",
Expand Down
14 changes: 14 additions & 0 deletions prisma/migrations/20240912180249_exports/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- CreateTable
CREATE TABLE "Export" (
"id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"complete" BOOLEAN NOT NULL DEFAULT false,
"path" TEXT NOT NULL,
"userId" INTEGER NOT NULL,

CONSTRAINT "Export_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "Export" ADD CONSTRAINT "Export_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
14 changes: 14 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ model User {
Invite Invite[]
Folder Folder[]
IncompleteFile IncompleteFile[]
Exports Export[]
}

model Export {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
complete Boolean @default(false)
path String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
}

model Folder {
Expand Down
29 changes: 28 additions & 1 deletion src/components/pages/Manage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
IconUserExclamation,
IconUserMinus,
IconUserX,
IconX,
} from '@tabler/icons-react';
import AnchorNext from 'components/AnchorNext';
import { FlameshotIcon, ShareXIcon } from 'components/icons';
Expand Down Expand Up @@ -264,14 +265,34 @@ export default function Manage({ oauth_registration, oauth_providers: raw_oauth_
setExports(
res.exports
?.map((s) => ({
date: new Date(Number(s.name.split('_')[3].slice(0, -4))),
date: new Date(s.createdAt),
size: s.size,
full: s.name,
}))
.sort((a, b) => a.date.getTime() - b.date.getTime()),
);
};

const deleteExport = async (name) => {
const res = await useFetch('/api/user/export?name=' + name, 'DELETE');
if (res.error) {
showNotification({
title: 'Error deleting export',
message: res.error,
color: 'red',
icon: <IconX size='1rem' />,
});
} else {
showNotification({
message: 'Deleted export',
color: 'green',
icon: <IconFileZip size='1rem' />,
});

await getExports();
}
};

const handleDelete = async () => {
const res = await useFetch('/api/user/files', 'DELETE', {
all: true,
Expand Down Expand Up @@ -580,6 +601,7 @@ export default function Manage({ oauth_registration, oauth_providers: raw_oauth_
{ id: 'name', name: 'Name' },
{ id: 'date', name: 'Date' },
{ id: 'size', name: 'Size' },
{ id: 'actions', name: '' },
]}
rows={
exports
Expand All @@ -591,6 +613,11 @@ export default function Manage({ oauth_registration, oauth_providers: raw_oauth_
),
date: x.date.toLocaleString(),
size: bytesToHuman(x.size),
actions: (
<ActionIcon onClick={() => deleteExport(x.full)}>
<IconTrash size='1rem' />
</ActionIcon>
),
}))
: []
}
Expand Down
92 changes: 75 additions & 17 deletions src/pages/api/user/export.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Zip, ZipPassThrough } from 'fflate';
import { createReadStream, createWriteStream } from 'fs';
import { readdir, stat } from 'fs/promises';
import { rm, stat } from 'fs/promises';
import datasource from 'lib/datasource';
import Logger from 'lib/logger';
import prisma from 'lib/prisma';
Expand All @@ -23,6 +23,13 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
const export_name = `zipline_export_${user.id}_${Date.now()}.zip`;
const path = join(config.core.temp_directory, export_name);

const exportDb = await prisma.export.create({
data: {
path: export_name,
userId: user.id,
},
});

logger.debug(`creating write stream at ${path}`);
const write_stream = createWriteStream(path);

Expand Down Expand Up @@ -79,11 +86,27 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
logger.info(
`Export for ${user.username} (${user.id}) has completed and is available at ${export_name}`,
);

await prisma.export.update({
where: {
id: exportDb.id,
},
data: {
complete: true,
},
});
}
} else {
write_stream.close();
logger.debug(`error while writing to zip: ${err}`);
logger.error(`Export for ${user.username} (${user.id}) has failed\n${err}`);
logger.error(
`Export for ${user.username} (${user.id}) has failed and has been removed from the database\n${err}`,
);

await prisma.export.delete({
where: {
id: exportDb.id,
},
});
}
};

Expand Down Expand Up @@ -114,27 +137,62 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
res.json({
url: '/api/user/export?name=' + export_name,
});
} else if (req.method === 'DELETE') {
const name = req.query.name as string;
if (!name) return res.badRequest('no name provided');

const exportDb = await prisma.export.findFirst({
where: {
userId: user.id,
path: name,
},
});

if (!exportDb) return res.notFound('export not found');

await prisma.export.delete({
where: {
id: exportDb.id,
},
});

try {
await rm(join(config.core.temp_directory, exportDb.path));
} catch (e) {
logger
.error(`export file ${exportDb.path} has been removed from the database`)
.error(`but failed to remove the file from the filesystem: ${e}`);
}

res.json({
success: true,
});
} else {
const export_name = req.query.name as string;
if (export_name) {
const parts = export_name.split('_');
if (Number(parts[2]) !== user.id) return res.unauthorized('cannot access export owned by another user');
const exportsDb = await prisma.export.findMany({
where: {
userId: user.id,
},
});

const name = req.query.name as string;
if (name) {
const exportDb = exportsDb.find((e) => e.path === name);
if (!exportDb) return res.notFound('export not found');

const stream = createReadStream(join(config.core.temp_directory, export_name));
const stream = createReadStream(join(config.core.temp_directory, exportDb.path));

res.setHeader('Content-Type', 'application/zip');
res.setHeader('Content-Disposition', `attachment; filename="${export_name}"`);
res.setHeader('Content-Disposition', `attachment; filename="${exportDb.path}"`);
stream.pipe(res);
} else {
const files = await readdir(config.core.temp_directory);
const exp = files.filter((f) => f.startsWith('zipline_export_'));
const exports = [];
for (let i = 0; i !== exp.length; ++i) {
const name = exp[i];
const stats = await stat(join(config.core.temp_directory, name));

if (Number(exp[i].split('_')[2]) !== user.id) continue;
exports.push({ name, size: stats.size });
for (let i = 0; i !== exportsDb.length; ++i) {
const exportDb = exportsDb[i];
if (!exportDb.complete) continue;

const stats = await stat(join(config.core.temp_directory, exportDb.path));
exports.push({ name: exportDb.path, size: stats.size, createdAt: exportDb.createdAt });
}

res.json({
Expand All @@ -145,6 +203,6 @@ async function handler(req: NextApiReq, res: NextApiRes, user: UserExtended) {
}

export default withZipline(handler, {
methods: ['GET', 'POST'],
methods: ['GET', 'POST', 'DELETE'],
user: true,
});
9 changes: 7 additions & 2 deletions src/pages/view/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,14 @@ export default function EmbeddedFile({

const img = new Image();
img.addEventListener('load', function () {
// my best attempt of recreating https://searchfox.org/mozilla-central/source/dom/html/ImageDocument.cpp#271-276
// and it actually works
// my best attempt of recreating
// firefox: https://searchfox.org/mozilla-central/source/dom/html/ImageDocument.cpp#271-276
// chromium-based: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/image_document.cc

// keeps image original if smaller than screen
if (this.width <= window.innerWidth && this.height <= window.innerHeight) return;

// resizes to fit screen
const ratio = Math.min(innerHeight / this.naturalHeight, innerWidth / this.naturalWidth);
const newWidth = Math.max(1, Math.floor(ratio * this.naturalWidth));
const newHeight = Math.max(1, Math.floor(ratio * this.naturalHeight));
Expand Down
Loading

0 comments on commit ed0af04

Please sign in to comment.