merge: dmgnr/image-optimization into dev [no ci]
@@ -39,6 +39,9 @@ jobs:
|
||||
-t mts.dgnr.us:5000/${{ gitea.repository }}:frontend \
|
||||
-t mts.dgnr.us:5000/${{ gitea.repository }}:frontend-$TAG \
|
||||
.
|
||||
docker build --target migration \
|
||||
-t mts.dgnr.us:5000/${{ gitea.repository }}:migration \
|
||||
.
|
||||
|
||||
build-backend:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -83,6 +86,7 @@ jobs:
|
||||
docker push mts.dgnr.us:5000/${{ gitea.repository }}:backend-$TAG &
|
||||
docker push mts.dgnr.us:5000/${{ gitea.repository }}:frontend &
|
||||
docker push mts.dgnr.us:5000/${{ gitea.repository }}:frontend-$TAG &
|
||||
docker push mts.dgnr.us:5000/${{ gitea.repository }}:migration &
|
||||
wait
|
||||
|
||||
- name: Install kubectl
|
||||
@@ -106,9 +110,12 @@ jobs:
|
||||
# DB migration
|
||||
kubectl delete job db-migrate -n buzz --ignore-not-found
|
||||
kubectl create job db-migrate \
|
||||
--image=mts.dgnr.us:5000/${{ gitea.repository }}:frontend-$TAG \
|
||||
--image=mts.dgnr.us:5000/${{ gitea.repository }}:migration \
|
||||
-n buzz \
|
||||
-- bun dr push
|
||||
--dry-run=client -o json \
|
||||
-- bun dr push | \
|
||||
jq '.spec.template.spec.containers[0].envFrom = [{"secretRef": {"name": "buzz-env"}}]' | \
|
||||
kubectl apply -f -
|
||||
|
||||
# Roll app & backend
|
||||
kubectl set image deployment/app app=mts.dgnr.us:5000/${{ gitea.repository }}:frontend-$TAG -n buzz
|
||||
|
||||
@@ -4,17 +4,6 @@
|
||||
FROM oven/bun:canary-alpine AS deps
|
||||
WORKDIR /home/container
|
||||
|
||||
# RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
# --mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
# apt-get update \
|
||||
# && apt-get install -y \
|
||||
# python3 \
|
||||
# make \
|
||||
# g++ \
|
||||
# sqlite3 \
|
||||
# libsqlite3-dev \
|
||||
# && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY package*.json bun.lock ./
|
||||
COPY patches ./patches
|
||||
|
||||
@@ -50,7 +39,22 @@ RUN --mount=type=cache,target=/home/container/.next/cache,uid=1001,gid=1001 \
|
||||
# Fix nextjs caching(bind mount removes it after build process)
|
||||
RUN mkdir -p .next/cache
|
||||
|
||||
### Stage 3: runner ###
|
||||
### Stage 3: migration ###
|
||||
FROM oven/bun:canary-alpine AS migration
|
||||
WORKDIR /home/container
|
||||
|
||||
COPY --from=deps /home/container/node_modules ./node_modules
|
||||
COPY --from=deps /home/container/package*.json ./
|
||||
COPY --from=deps /home/container/bun.lock ./
|
||||
COPY --from=builder /home/container/drizzle.config.ts ./drizzle.config.ts
|
||||
COPY --from=builder /home/container/lib ./lib
|
||||
|
||||
COPY drizzle.config.ts ./
|
||||
COPY lib/db/ ./lib/db/
|
||||
|
||||
CMD ["bun", "drizzle-kit"]
|
||||
|
||||
### Stage 4: runner ###
|
||||
FROM oven/bun:canary-alpine AS runner
|
||||
|
||||
# Isolation
|
||||
@@ -58,19 +62,10 @@ RUN adduser -Du 1001 container
|
||||
USER container
|
||||
WORKDIR /home/container
|
||||
|
||||
COPY --from=builder /home/container/node_modules ./node_modules
|
||||
COPY --from=builder /home/container/next.config.ts ./
|
||||
COPY --from=builder /home/container/bun.lock ./
|
||||
COPY --from=builder /home/container/package*.json ./
|
||||
|
||||
COPY --from=builder /home/container/.next/standalone ./
|
||||
COPY --from=builder /home/container/public ./public
|
||||
COPY --from=builder /home/container/.env ./.env
|
||||
COPY --from=builder /home/container/.next ./.next
|
||||
|
||||
COPY --from=builder /home/container/util ./util
|
||||
COPY --from=builder /home/container/lib ./lib
|
||||
COPY --from=builder /home/container/tsconfig.json ./tsconfig.json
|
||||
COPY --from=builder /home/container/.next/static ./.next/static
|
||||
COPY --from=builder /home/container/.version ./.version
|
||||
COPY --from=builder /home/container/drizzle.config.ts ./drizzle.config.ts
|
||||
|
||||
CMD ["bun", "start"]
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
CMD ["bun", "server.js"]
|
||||
@@ -0,0 +1,72 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Background0 from "#/bg.webp";
|
||||
import Background1 from "#/bgassets/buzzbg1-r2.webp";
|
||||
import Background2 from "#/bgassets/buzzbg2-r1.webp";
|
||||
import Background3 from "#/bgassets/buzzbg3-r1.webp";
|
||||
import Background4 from "#/bgassets/buzzbg4-r1.webp";
|
||||
import Background5 from "#/bgassets/buzzbg5-r1.webp";
|
||||
import Background6 from "#/bgassets/buzzbg6-r1.webp";
|
||||
import Background7 from "#/bgassets/buzzbg7-r1.webp";
|
||||
import type { StaticImport } from "next/dist/shared/lib/get-img-props";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useMemo, useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { StarsRenderer } from "@/components/stars";
|
||||
|
||||
type PrefixBackground = [`/${string}`, string | StaticImport, `/${string}`];
|
||||
const backgrounds = [
|
||||
// path prefix, background, stars mask
|
||||
["/artifact", Background1, "/bgassets/buzzbg1-r2-mask.webp"],
|
||||
["/rubgram", Background2, "/bgassets/buzzbg2-r1-mask.webp"],
|
||||
["/donate", Background3, "/bgassets/buzzbg3-r1-mask.webp"],
|
||||
["/tl", Background5, "/bgassets/buzzbg4-r1-mask.webp"],
|
||||
["/guide", Background4, "/bgassets/buzzbg5-r1-mask.webp"],
|
||||
["/admin", Background6, "/bgassets/buzzbg6-r1-mask.webp"],
|
||||
// unused
|
||||
["/donate", Background7, "/bgassets/buzzbg7-r1-mask.webp"],
|
||||
// this should be last
|
||||
["/", Background0, "/mask.webp"],
|
||||
] satisfies PrefixBackground[];
|
||||
|
||||
export function BackgroundProvider() {
|
||||
const pathname = usePathname();
|
||||
const [, background, mask] = useMemo(
|
||||
() => backgrounds.find((b) => pathname.startsWith(b[0]))!,
|
||||
[pathname],
|
||||
);
|
||||
const [transitioning, setTransitioning] = useState(false);
|
||||
const [current, setCurrent] = useState<string | StaticImport>(background);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src={background}
|
||||
alt="Background Swap"
|
||||
className={cn(
|
||||
"fixed top-0 left-0 z-[-1] h-fit min-h-dvh w-full object-cover opacity-0",
|
||||
transitioning && "opacity-40 transition-opacity duration-500",
|
||||
)}
|
||||
onLoad={() => {
|
||||
if (background === current) return;
|
||||
setTransitioning(true);
|
||||
}}
|
||||
onTransitionEnd={() => {
|
||||
if (!transitioning) return;
|
||||
setCurrent(background);
|
||||
setTransitioning(false);
|
||||
}}
|
||||
/>
|
||||
<Image
|
||||
src={current}
|
||||
alt="Background Current"
|
||||
className={cn(
|
||||
"fixed top-0 left-0 z-[-1] h-fit min-h-dvh w-full object-cover opacity-40",
|
||||
transitioning && "opacity-0 transition-opacity duration-500",
|
||||
)}
|
||||
/>
|
||||
{transitioning || <StarsRenderer mask={mask} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
import { Anuphan } from "next/font/google";
|
||||
import { ViewTransition } from "react";
|
||||
import { Toaster } from "sonner";
|
||||
import Background from "#/bg.webp";
|
||||
import Image from "@/components/image";
|
||||
import { StarsRenderer } from "@/components/stars";
|
||||
import Providers, { VersionCheck } from "../client";
|
||||
import { Navbar } from "./navbar";
|
||||
import { BackgroundProvider } from "./background";
|
||||
|
||||
const anuphan = Anuphan({
|
||||
variable: "--font-anuphan",
|
||||
@@ -19,12 +17,7 @@ export default async function UiLayout({
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src={Background}
|
||||
alt="Background"
|
||||
className="fixed top-0 left-0 z-[-1] h-fit min-h-dvh w-full object-cover opacity-40"
|
||||
/>
|
||||
<StarsRenderer />
|
||||
<BackgroundProvider />
|
||||
<Providers>
|
||||
<ViewTransition>{children}</ViewTransition>
|
||||
<VersionCheck />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function StarsRenderer() {
|
||||
export function StarsRenderer({ mask }: { mask: string }) {
|
||||
const cv = useRef<HTMLCanvasElement | null>(null);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
@@ -122,9 +122,12 @@ export function StarsRenderer() {
|
||||
<canvas
|
||||
ref={cv}
|
||||
className={cn(
|
||||
"fixed top-0 left-0 z-[-1] h-fit min-h-dvh w-full mask-[url('/mask.webp')] mask-cover mask-center mask-no-repeat object-cover opacity-0 transition-opacity duration-5000",
|
||||
"fixed top-0 left-0 z-[-1] h-fit min-h-dvh w-full mask-cover mask-center mask-no-repeat object-cover opacity-0 transition-opacity duration-5000",
|
||||
loaded && "opacity-40",
|
||||
)}
|
||||
style={{
|
||||
maskImage: `url(${JSON.stringify(mask)})`,
|
||||
}}
|
||||
></canvas>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,4 +52,5 @@ export default {
|
||||
// reactCompiler: true,
|
||||
allowedDevOrigins: ["astral:3000", "dev3000.astrxl.dev"],
|
||||
// basePath: "/beta"
|
||||
output: "standalone",
|
||||
} satisfies NextConfig;
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 839 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 1016 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,14 @@
|
||||
import { file, Glob, write } from "bun";
|
||||
import { rm } from "node:fs/promises";
|
||||
|
||||
const g = new Glob("public/bgassets/*.png");
|
||||
|
||||
for await (const img of g.scan()) {
|
||||
file(img)
|
||||
.image()
|
||||
.webp()
|
||||
.buffer()
|
||||
.then((b) => write(img.replace(".png", ".webp"), b))
|
||||
.then(() => rm(img))
|
||||
.finally(() => console.log(img, "processed."));
|
||||
}
|
||||