mirror of
https://github.com/fccview/cronmaster.git
synced 2026-05-19 04:09:16 -04:00
create tarball for next release and fix few minor issues, also add download logs
This commit is contained in:
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: fccview
|
||||
2
.github/workflows/docker-build.yml
vendored
2
.github/workflows/docker-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Reusable Docker Build Logic
|
||||
name: Builder
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build and Publish Multi-Platform Docker Image
|
||||
name: Build and Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
48
.github/workflows/pr-checks.yml
vendored
Normal file
48
.github/workflows/pr-checks.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: PR Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ["**"]
|
||||
|
||||
jobs:
|
||||
validate-branch:
|
||||
name: Validate Target Branch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: YOU SHALL NOT PASS
|
||||
if: github.base_ref == 'main'
|
||||
run: |
|
||||
if [ "${{ github.head_ref }}" != "develop" ]; then
|
||||
echo "ERROR: Pull requests to 'main' are not allowed."
|
||||
echo "Current source branch: ${{ github.head_ref }}"
|
||||
echo ""
|
||||
echo "Please create a PR to 'develop' first, this will become a release candidate when merged into 'main' by a maintainer"
|
||||
exit 1
|
||||
fi
|
||||
echo "Valid PR: develop → main"
|
||||
|
||||
- name: PR info
|
||||
run: |
|
||||
echo "PR validation passed"
|
||||
echo "Source: ${{ github.head_ref }}"
|
||||
echo "Target: ${{ github.base_ref }}"
|
||||
|
||||
typing:
|
||||
name: Type and install checks
|
||||
runs-on: ubuntu-latest
|
||||
needs: validate-branch
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the best engine ever
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install all dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: This will totally fail if you only use AI
|
||||
run: yarn tsc --noEmit
|
||||
60
.github/workflows/prebuild-release.yml
vendored
Normal file
60
.github/workflows/prebuild-release.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Build and Release Prebuilt Tarball
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["*"]
|
||||
|
||||
jobs:
|
||||
build-prebuild:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup the best engine ever
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Bake cronmaster
|
||||
env:
|
||||
NODE_ENV: production
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
run: yarn build
|
||||
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Structure the prebuild stuff
|
||||
run: |
|
||||
mkdir -p prebuild-release/cronmaster/.next
|
||||
cp -r .next/standalone/. prebuild-release/cronmaster/
|
||||
cp -r .next/static prebuild-release/cronmaster/.next/static
|
||||
if [ -f .next/BUILD_ID ]; then
|
||||
cp .next/BUILD_ID prebuild-release/cronmaster/.next/BUILD_ID
|
||||
fi
|
||||
cp -r public prebuild-release/cronmaster/public
|
||||
cp -r howto prebuild-release/cronmaster/howto
|
||||
|
||||
- name: Create tarball - tarball is a funny name
|
||||
run: |
|
||||
cd prebuild-release
|
||||
tar -czf cronmaster_${{ steps.version.outputs.version }}_prebuild.tar.gz cronmaster
|
||||
sha256sum cronmaster_${{ steps.version.outputs.version }}_prebuild.tar.gz > cronmaster_${{ steps.version.outputs.version }}_prebuild.tar.gz.sha256
|
||||
|
||||
- name: Attach to Release - pray it works
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
prebuild-release/cronmaster_*_prebuild.tar.gz
|
||||
prebuild-release/cronmaster_*_prebuild.tar.gz.sha256
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
@@ -92,7 +92,7 @@ export const Sidebar = forwardRef<HTMLDivElement, SidebarProps>(
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
className="absolute -right-3 top-[21.5vh] w-6 h-6 bg-background0 ascii-border items-center justify-center transition-colors z-40 hidden lg:flex"
|
||||
className="sidebar-shrinker absolute -right-3 top-[21.5vh] w-6 h-6 bg-background0 ascii-border items-center justify-center transition-colors z-40 hidden lg:flex"
|
||||
>
|
||||
{isCollapsed ? (
|
||||
<CaretRightIcon className="h-3 w-3" />
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Modal } from "@/app/_components/GlobalComponents/UIElements/Modal";
|
||||
import { Button } from "@/app/_components/GlobalComponents/UIElements/Button";
|
||||
import { FileTextIcon, TrashIcon, EyeIcon, XIcon, ArrowsClockwiseIcon, WarningCircleIcon, CheckCircleIcon } from "@phosphor-icons/react";
|
||||
import { FileTextIcon, TrashIcon, EyeIcon, XIcon, ArrowsClockwiseIcon, WarningCircleIcon, CheckCircleIcon, DownloadIcon } from "@phosphor-icons/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { zipSync, strToU8 } from "fflate";
|
||||
import {
|
||||
getJobLogs,
|
||||
getLogContent,
|
||||
@@ -44,6 +45,7 @@ export const LogsModal = ({
|
||||
const [logContent, setLogContent] = useState<string>("");
|
||||
const [isLoadingLogs, setIsLoadingLogs] = useState(false);
|
||||
const [isLoadingContent, setIsLoadingContent] = useState(false);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const [stats, setStats] = useState<{
|
||||
count: number;
|
||||
totalSize: number;
|
||||
@@ -133,6 +135,28 @@ export const LogsModal = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownloadLogs = async () => {
|
||||
if (logs.length === 0) return;
|
||||
setIsDownloading(true);
|
||||
try {
|
||||
const files: Record<string, Uint8Array> = {};
|
||||
for (const log of logs) {
|
||||
const content = await getLogContent(jobId, log.filename);
|
||||
files[log.filename] = strToU8(content);
|
||||
}
|
||||
const zipped = zipSync(files);
|
||||
const blob = new Blob([zipped as unknown as ArrayBuffer], { type: "application/zip" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `${jobComment || jobId}_logs.zip`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} finally {
|
||||
setIsDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
||||
@@ -157,16 +181,29 @@ export const LogsModal = ({
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} title={t("cronjobs.viewLogs")} size="xl">
|
||||
<div className="flex flex-col h-[600px]">
|
||||
<div className="flex items-center justify-between mb-4 pb-4 border-b border-border">
|
||||
<div>
|
||||
<h3 className="font-semibold text-lg">{jobComment || jobId}</h3>
|
||||
<div className="block sm:flex items-center justify-between mb-4 pb-4 border-b border-border">
|
||||
<div className="min-w-0 mb-4 sm:mb-0">
|
||||
<h3 className="font-semibold text-lg truncate">{jobComment || jobId}</h3>
|
||||
{stats && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{stats.count} {t("cronjobs.logs")} • {stats.totalSizeMB} MB
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex gap-2 flex-shrink-0">
|
||||
<Button
|
||||
onClick={handleDownloadLogs}
|
||||
disabled={logs.length === 0 || isDownloading}
|
||||
className="btn-primary glow-primary"
|
||||
size="sm"
|
||||
>
|
||||
{isDownloading ? (
|
||||
<ArrowsClockwiseIcon className="w-4 h-4 sm:mr-2 animate-spin" />
|
||||
) : (
|
||||
<DownloadIcon className="w-4 h-4 sm:mr-2" />
|
||||
)}
|
||||
<span className="hidden sm:inline">{t("cronjobs.downloadLog")}</span>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={loadLogs}
|
||||
disabled={isLoadingLogs}
|
||||
@@ -174,10 +211,10 @@ export const LogsModal = ({
|
||||
size="sm"
|
||||
>
|
||||
<ArrowsClockwiseIcon
|
||||
className={`w-4 h-4 mr-2 ${isLoadingLogs ? "animate-spin" : ""
|
||||
className={`w-4 h-4 sm:mr-2 ${isLoadingLogs ? "animate-spin" : ""
|
||||
}`}
|
||||
/>
|
||||
{t("common.refresh")}
|
||||
<span className="hidden sm:inline">{t("common.refresh")}</span>
|
||||
</Button>
|
||||
{logs.length > 0 && (
|
||||
<Button
|
||||
@@ -185,15 +222,15 @@ export const LogsModal = ({
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
>
|
||||
<TrashIcon className="w-4 h-4 mr-2" />
|
||||
{t("cronjobs.deleteAll")}
|
||||
<TrashIcon className="w-4 h-4 sm:mr-2" />
|
||||
<span className="hidden sm:inline">{t("cronjobs.deleteAll")}</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex gap-4 overflow-hidden">
|
||||
<div className="w-1/3 flex flex-col border-r border-border pr-4 overflow-hidden">
|
||||
<div className="flex-1 flex flex-col sm:flex-row gap-4 overflow-hidden">
|
||||
<div className="sm:w-1/3 flex flex-col sm:border-r border-b sm:border-b-0 border-border sm:pr-4 pb-4 sm:pb-0 overflow-hidden max-h-[40%] sm:max-h-none">
|
||||
<h4 className="font-semibold mb-2">{t("cronjobs.logFiles")}</h4>
|
||||
<div className="flex-1 overflow-y-auto space-y-2">
|
||||
{isLoadingLogs ? (
|
||||
@@ -288,8 +325,8 @@ export const LogsModal = ({
|
||||
|
||||
<div className="mt-4 pt-4 border-t border-border flex justify-end">
|
||||
<Button onClick={onClose} className="btn-primary glow-primary">
|
||||
<XIcon className="w-4 h-4 mr-2" />
|
||||
{t("common.close")}
|
||||
<XIcon className="w-4 h-4 sm:mr-2" />
|
||||
<span className="hidden sm:inline">{t("common.close")}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"logs": "logs",
|
||||
"logFiles": "Log Files",
|
||||
"logContent": "Log Content",
|
||||
"downloadLog": "Download",
|
||||
"selectLogToView": "Select a log file to view its content",
|
||||
"noLogsFound": "No logs found for this job",
|
||||
"confirmDeleteLog": "Are you sure you want to delete this log file?",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"logs": "log",
|
||||
"logFiles": "File",
|
||||
"logContent": "Contenuto Log",
|
||||
"downloadLog": "Scarica",
|
||||
"selectLogToView": "Seleziona un file per visualizzarne il contenuto",
|
||||
"noLogsFound": "Nessun log trovato per questa operazione",
|
||||
"confirmDeleteLog": "Sei sicuro di voler eliminare questo file?",
|
||||
|
||||
@@ -260,4 +260,20 @@ pre::-webkit-scrollbar {
|
||||
max-width: 100%;
|
||||
margin: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-shrinker {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.sidebar-shrinker:before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 6px solid var(--box-border-color, var(--foreground2));
|
||||
border-right: 12px solid transparent;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
bottom: -6px;
|
||||
z-index: -1;
|
||||
}
|
||||
@@ -81,7 +81,9 @@ export default async function Home() {
|
||||
Cr<span className="text-status-error">*</span>nMaster
|
||||
</h1>
|
||||
<p className="text-xs terminal-font flex items-center gap-2">
|
||||
{t("common.version").replace("{version}", version)}
|
||||
<a href={`https://github.com/fccview/cronmaster/releases/tag/${version}`} target="_blank" rel="noopener noreferrer">
|
||||
{t("common.version").replace("{version}", version)}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cronjob-manager",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -34,6 +34,7 @@
|
||||
"codemirror": "^6.0.2",
|
||||
"cron-parser": "^5.3.0",
|
||||
"cronstrue": "^3.2.0",
|
||||
"fflate": "^0.8.2",
|
||||
"jose": "^6.1.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"next": "14.2.35",
|
||||
|
||||
@@ -2842,6 +2842,11 @@ fdir@^6.5.0:
|
||||
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
|
||||
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
|
||||
|
||||
fflate@^0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
|
||||
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
|
||||
Reference in New Issue
Block a user