Compare commits

...

3 Commits
dev ... v0.2.1

Author SHA1 Message Date
David Girón
2b325f3461 feat: optimize Dockerfile (#47)
* define base image arg

* create base stage with common content

* chmod executable entrypoint file

this avoids re-copying the same file as is being modified in the docker
layer

* cache npm downloaded packages

avoids re-downloading deps if cache content is available
2025-08-19 12:17:32 +03:00
Til Wegener
4d3c164bc0 Fix UI size display and ingestion history graph (#50)
* fix: unify size display, improve graph interpolation & time readability

* fix display human-readable sizes in ingestion chart

* display human-readable sizes in ingestion chart

* fix: format code

* fix keep fallback for item.name
2025-08-19 11:06:31 +03:00
Wei S.
7288286fd9 Format checked, contributing.md update (#49)
Co-authored-by: Wayne <5291640+ringoinca@users.noreply.github.com>
2025-08-17 17:42:49 +03:00
10 changed files with 51 additions and 33 deletions

View File

@@ -51,3 +51,13 @@ This project and everyone participating in it is governed by the [Open Archiver
- Follow the existing code style.
- Use TypeScript's strict mode.
- Avoid using `any` as a type. Define clear interfaces and types in the `packages/types` directory.
### Formatting
We use Prettier for code formatting. Before you commit new code, it is necessary to check code format by running this command from the root folder:
`pnpm run lint`
If there are any format issues, you can use the following command to fix them
`pnpm run format`

View File

@@ -1,21 +1,29 @@
# Dockerfile for Open Archiver
# 1. Build Stage: Install all dependencies and build the project
FROM node:22-alpine AS build
ARG BASE_IMAGE=node:22-alpine
# 0. Base Stage: Define all common dependencies and setup
FROM ${BASE_IMAGE} AS base
WORKDIR /app
# Install pnpm
RUN npm install -g pnpm
RUN --mount=type=cache,target=/root/.npm \
npm install -g pnpm
# Copy manifests and lockfile
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY packages/backend/package.json ./packages/backend/
COPY packages/frontend/package.json ./packages/frontend/
COPY packages/types/package.json ./packages/types/
# 1. Build Stage: Install all dependencies and build the project
FROM base AS build
COPY packages/frontend/svelte.config.js ./packages/frontend/
# Install all dependencies. Use --shamefully-hoist to create a flat node_modules structure
RUN pnpm install --shamefully-hoist --frozen-lockfile --prod=false
ENV PNPM_HOME="/pnpm"
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --shamefully-hoist --frozen-lockfile --prod=false
# Copy the rest of the source code
COPY . .
@@ -24,17 +32,7 @@ COPY . .
RUN pnpm build
# 2. Production Stage: Install only production dependencies and copy built artifacts
FROM node:22-alpine AS production
WORKDIR /app
# Install pnpm
RUN npm install -g pnpm
# Copy manifests and lockfile
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY packages/backend/package.json ./packages/backend/
COPY packages/frontend/package.json ./packages/frontend/
COPY packages/types/package.json ./packages/types/
FROM base AS production
# Install production dependencies
# RUN pnpm install --shamefully-hoist --frozen-lockfile --prod=true
@@ -48,7 +46,6 @@ COPY --from=build /app/packages/backend/src/database/migrations ./packages/backe
# Copy the entrypoint script and make it executable
COPY docker/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Expose the port the app runs on
EXPOSE 4000

0
docker/docker-entrypoint.sh Normal file → Executable file
View File

View File

@@ -33,13 +33,10 @@ export class IngestionController {
} catch (error: any) {
logger.error({ err: error }, 'Create ingestion source error');
// Return a 400 Bad Request for connection errors
return res
.status(400)
.json({
message:
error.message ||
'Failed to create ingestion source due to a connection error.',
});
return res.status(400).json({
message:
error.message || 'Failed to create ingestion source due to a connection error.',
});
}
};

View File

@@ -193,7 +193,6 @@ export class ImapConnector implements IEmailConnector {
// Initialize with last synced UID, not the maximum UID in mailbox
this.newMaxUids[mailboxPath] = lastUid || 0;
// Only fetch if the mailbox has messages, to avoid errors on empty mailboxes with some IMAP servers.
if (mailbox.exists > 0) {
const BATCH_SIZE = 250; // A configurable batch size

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import * as Chart from '$lib/components/ui/chart/index.js';
import { AreaChart } from 'layerchart';
import { curveCatmullRom } from 'd3-shape';
import { curveMonotoneX } from 'd3-shape';
import type { ChartConfig } from '$lib/components/ui/chart';
export let data: { date: Date; count: number }[];
@@ -39,16 +39,24 @@
props={{
xAxis: {
format: (d) =>
new Date(d).toLocaleDateString('en-US', {
new Date(d).toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
}),
},
area: { curve: curveCatmullRom },
area: { curve: curveMonotoneX },
}}
>
{#snippet tooltip()}
<Chart.Tooltip />
<Chart.Tooltip
labelFormatter={(value) =>
(value instanceof Date ? value : new Date(value)).toLocaleString(undefined, {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
/>
{/snippet}
</AreaChart>
</Chart.Container>

View File

@@ -3,6 +3,7 @@
import { PieChart } from 'layerchart';
import type { IngestionSourceStats } from '@open-archiver/types';
import type { ChartConfig } from '$lib/components/ui/chart';
import { formatBytes } from '$lib/utils';
export let data: IngestionSourceStats[];
@@ -29,7 +30,11 @@
]}
>
{#snippet tooltip()}
<Chart.Tooltip></Chart.Tooltip>
<Chart.Tooltip>
{#snippet formatter({ value, item })}
{item.payload.name}: {formatBytes(value as number)}
{/snippet}
</Chart.Tooltip>
{/snippet}
</PieChart>
</Chart.Container>

View File

@@ -105,10 +105,10 @@
indicator === "dot" && "items-center"
)}
>
{#if formatter && item.value !== undefined && item.name}
{#if formatter && item.value !== undefined}
{@render formatter({
value: item.value,
name: item.name,
name: item.name || '',
item,
index: i,
payload: tooltipCtx.payload,

View File

@@ -111,7 +111,7 @@
<div class=" lg:col-span-1">
<Card.Root class="h-full">
<Card.Header>
<Card.Title>Storage by Ingestion Source (Bytes)</Card.Title>
<Card.Title>Storage by Ingestion Source</Card.Title>
</Card.Header>
<Card.Content class="h-full">
{#if data.ingestionSources && data.ingestionSources.length > 0}

View File

@@ -128,7 +128,9 @@
class="flex items-center justify-between rounded-md border p-2"
>
<span
>{attachment.filename} ({attachment.sizeBytes} bytes)</span
>{attachment.filename} ({formatBytes(
attachment.sizeBytes
)})</span
>
<Button
variant="outline"