Compare commits
6 Commits
9d0017a1b6
...
SetUpInfin
| Author | SHA1 | Date | |
|---|---|---|---|
| e514d05fd6 | |||
| 6eaf1d6b9f | |||
| ce19237fd2 | |||
| 06d75f9d29 | |||
| dc195aecc3 | |||
| a591fe4157 |
@@ -40,12 +40,12 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
'gitea.home.joemonk.co.uk/${{ gitea.repository }}:latest'
|
gitea.home.joemonk.co.uk/${{ gitea.repository }}:latest
|
||||||
'gitea.home.joemonk.co.uk/${{ gitea.repository }}:${{ gitea.sha }}'
|
gitea.home.joemonk.co.uk/${{ gitea.repository }}:${{ gitea.sha }}
|
||||||
'registry.fly.io/${{ gitea.repository }}:${{ gitea.sha }}'
|
registry.fly.io/joemonk:${{ gitea.sha }}
|
||||||
|
|
||||||
- uses: superfly/flyctl-actions/setup-flyctl@master
|
- uses: superfly/flyctl-actions/setup-flyctl@master
|
||||||
|
|
||||||
- run: flyctl deploy --remote-only -i registry.fly.io/${{ gitea.repository }}:${{ gitea.sha }}
|
- run: flyctl deploy --remote-only -i registry.fly.io/joemonk:${{ gitea.sha }}
|
||||||
env:
|
env:
|
||||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||||
10
Dockerfile
10
Dockerfile
@@ -15,7 +15,7 @@ WORKDIR /app
|
|||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@ RUN npm run build
|
|||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV=production
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
RUN addgroup --system --gid 1001 nodejs
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
RUN adduser --system --uid 1001 nextjs
|
RUN adduser --system --uid 1001 nextjs
|
||||||
@@ -39,13 +39,13 @@ RUN chown nextjs:nodejs .next
|
|||||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
COPY --from=builder /app/db.sql ./db.sql
|
COPY --from=builder --chown=nextjs:nodejs /app/db.sql ./db.sql
|
||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
ENV PORT 3000
|
ENV PORT=3000
|
||||||
|
|
||||||
# server.js is created by next build from the standalone output
|
# server.js is created by next build from the standalone output
|
||||||
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||||
|
|||||||
8
fly.toml
8
fly.toml
@@ -6,17 +6,15 @@
|
|||||||
app = 'joemonk'
|
app = 'joemonk'
|
||||||
primary_region = 'lhr'
|
primary_region = 'lhr'
|
||||||
|
|
||||||
[build]
|
|
||||||
|
|
||||||
[http_service]
|
[http_service]
|
||||||
internal_port = 3000
|
internal_port = 3000
|
||||||
force_https = true
|
force_https = true
|
||||||
auto_stop_machines = 'stop'
|
auto_stop_machines = 'stop'
|
||||||
auto_start_machines = true
|
auto_start_machines = true
|
||||||
min_machines_running = 0
|
min_machines_running = 1
|
||||||
processes = ['app']
|
processes = ["app"]
|
||||||
[[http_service.checks]]
|
[[http_service.checks]]
|
||||||
grace_period = "15s"
|
grace_period = "30s"
|
||||||
interval = "120s"
|
interval = "120s"
|
||||||
method = "GET"
|
method = "GET"
|
||||||
timeout = "5s"
|
timeout = "5s"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const nextConfig = {
|
|||||||
reactCompiler: true,
|
reactCompiler: true,
|
||||||
ppr: "incremental",
|
ppr: "incremental",
|
||||||
},
|
},
|
||||||
serverExternalPackages: ["typeorm", "better-sqlite3"],
|
serverExternalPackages: ["better-sqlite3"],
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
images: {
|
images: {
|
||||||
|
|||||||
822
package-lock.json
generated
822
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
src/app/(root)/manage/page.tsx
Normal file
14
src/app/(root)/manage/page.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { TRPCProvider } from "@/trpc/client";
|
||||||
|
import RefreshImageDb from "@/components/refresh-image-db";
|
||||||
|
import { trpc } from "@/trpc/server";
|
||||||
|
|
||||||
|
export default async function Photos(): Promise<React.JSX.Element> {
|
||||||
|
const imageCount = await trpc.photos.count();
|
||||||
|
return (
|
||||||
|
<div className="mx-auto">
|
||||||
|
<TRPCProvider>
|
||||||
|
<RefreshImageDb count={imageCount}/>
|
||||||
|
</TRPCProvider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,22 +9,7 @@ export default async function Photos(): Promise<React.JSX.Element> {
|
|||||||
return (
|
return (
|
||||||
<div className="mx-auto">
|
<div className="mx-auto">
|
||||||
<TRPCProvider>
|
<TRPCProvider>
|
||||||
<FilteredLightbox imageData={images}>
|
<FilteredLightbox imageData={images}/>
|
||||||
{images.map((image) => (
|
|
||||||
<Image
|
|
||||||
key={image.src}
|
|
||||||
alt={image.src}
|
|
||||||
src={image.src}
|
|
||||||
className="object-contain h-60 w-80"
|
|
||||||
sizes="100vw"
|
|
||||||
loading="lazy"
|
|
||||||
width={image.width}
|
|
||||||
height={image.height}
|
|
||||||
blurDataURL={image.blur}
|
|
||||||
placeholder="blur"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</FilteredLightbox>
|
|
||||||
</TRPCProvider>
|
</TRPCProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default function NavBar(): React.JSX.Element {
|
|||||||
<footer className="dark:bg-dracula-bg-darker border-t-2 dark:border-dracula-purple">
|
<footer className="dark:bg-dracula-bg-darker border-t-2 dark:border-dracula-purple">
|
||||||
<div className="mx-auto max-w-7xl px-4">
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
<div className="relative flex flex-row-reverse h-12 items-center justify-between">
|
<div className="relative flex flex-row-reverse h-12 items-center justify-between">
|
||||||
<span className='dark:text-white select-none'>© Joe Monk 2024</span>
|
<span className='dark:text-white select-none'>© Joe Monk 2025</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -129,15 +129,15 @@ interface UsernameFormElement extends HTMLFormElement {
|
|||||||
|
|
||||||
export default function FilteredLightbox(props: {
|
export default function FilteredLightbox(props: {
|
||||||
imageData: ImageData[];
|
imageData: ImageData[];
|
||||||
children: React.JSX.Element[];
|
|
||||||
}): React.JSX.Element {
|
}): React.JSX.Element {
|
||||||
//const [imageData, setImageData] = useState(props.imageData);
|
|
||||||
const [imageData] = useState(props.imageData);
|
|
||||||
const photoQuery = trpc.photos.list.useInfiniteQuery(
|
const photoQuery = trpc.photos.list.useInfiniteQuery(
|
||||||
{
|
{
|
||||||
limit: 1,
|
limit: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
initialData: {
|
initialData: {
|
||||||
pages: [
|
pages: [
|
||||||
{
|
{
|
||||||
@@ -151,24 +151,30 @@ export default function FilteredLightbox(props: {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshQuery = trpc.photos.update.useQuery(undefined, {
|
// Call from observable
|
||||||
enabled: false,
|
function loadNextPage(): void {
|
||||||
retry: false,
|
console.log(photoQuery.isFetchingNextPage);
|
||||||
});
|
console.log(photoQuery.hasNextPage);
|
||||||
|
if (!photoQuery.isFetchingNextPage) {
|
||||||
|
void photoQuery.fetchNextPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleSubmit(event: React.FormEvent<UsernameFormElement>): void {
|
function handleSubmit(event: React.FormEvent<UsernameFormElement>): void {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// const imageData = props.imageData;
|
// Set filters, then action in the re-render on the data, or pass to photo query?
|
||||||
// setImageData(
|
// setFilters(
|
||||||
// imageData.filter(
|
// imageData.filter(
|
||||||
// (data) => data.src === event.currentTarget.elements.src.value
|
// (data) => data.src === event.currentTarget.elements.src.value
|
||||||
// )
|
// )
|
||||||
// );
|
// );
|
||||||
void photoQuery.fetchNextPage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const children = photoQuery.data.pages
|
const imageData = photoQuery.data.pages
|
||||||
.flatMap((data) => data.data)
|
.flatMap((data) => data.data)
|
||||||
|
.filter((data) => !!data);
|
||||||
|
|
||||||
|
const images = imageData
|
||||||
.map((data) => (
|
.map((data) => (
|
||||||
<Image
|
<Image
|
||||||
key={data.src}
|
key={data.src}
|
||||||
@@ -182,8 +188,7 @@ export default function FilteredLightbox(props: {
|
|||||||
blurDataURL={data.blur}
|
blurDataURL={data.blur}
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
.filter((data) => !!data);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -194,16 +199,7 @@ export default function FilteredLightbox(props: {
|
|||||||
</div>
|
</div>
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
<button
|
<Lightbox imageData={imageData}>{...images}</Lightbox>
|
||||||
onClick={() => {
|
|
||||||
void refreshQuery.refetch();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
{refreshQuery.data ? JSON.stringify(refreshQuery.data) : "\nNot"}
|
|
||||||
{refreshQuery.error ? JSON.stringify(refreshQuery.error) : "\nNo Error"}
|
|
||||||
<Lightbox imageData={imageData}>{...children}</Lightbox>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/components/refresh-image-db.tsx
Normal file
30
src/components/refresh-image-db.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { trpc } from "@/trpc/client";
|
||||||
|
|
||||||
|
export default function RefreshImageDb({count}: {count: number}): React.JSX.Element {
|
||||||
|
const countQuery = trpc.photos.count.useQuery(undefined, {
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchOnReconnect: false,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
initialData: count
|
||||||
|
});
|
||||||
|
const refreshQuery = trpc.photos.update.useQuery(undefined, {
|
||||||
|
enabled: false,
|
||||||
|
retry: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{countQuery.data}
|
||||||
|
<button onClick={() => void refreshQuery.refetch()}>
|
||||||
|
Refresh image db
|
||||||
|
</button>
|
||||||
|
{refreshQuery.data ? JSON.stringify(refreshQuery.data) : "\nNot"}
|
||||||
|
{refreshQuery.error ? JSON.stringify(refreshQuery.error) : "\nNo Error"}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { createTRPCRouter, privateProcedure, publicProcedure } from '../init';
|
|||||||
|
|
||||||
import { list } from './photos/list';
|
import { list } from './photos/list';
|
||||||
import { update } from './photos/update';
|
import { update } from './photos/update';
|
||||||
|
import { count } from './photos/count';
|
||||||
|
|
||||||
export const photosRouter = createTRPCRouter({
|
export const photosRouter = createTRPCRouter({
|
||||||
list: publicProcedure
|
list: publicProcedure
|
||||||
@@ -31,5 +32,6 @@ export const photosRouter = createTRPCRouter({
|
|||||||
next
|
next
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
count: publicProcedure.query(count),
|
||||||
update: privateProcedure.query(update)
|
update: privateProcedure.query(update)
|
||||||
});
|
});
|
||||||
7
src/trpc/routers/photos/count/index.ts
Normal file
7
src/trpc/routers/photos/count/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import db from "@/db/db";
|
||||||
|
import { photosTable } from "@/db/schema/photo";
|
||||||
|
|
||||||
|
export async function count(): Promise<number> {
|
||||||
|
const count = await db.$count(photosTable);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user