fix(ci): strip 'build' from .dockerignore before docker-ui build
All checks were successful
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 1m41s
Release / Docker / caddy (push) Successful in 41s
Release / Docker / backend (push) Successful in 3m23s
Release / Docker / runner (push) Successful in 2m53s
Release / Upload source maps (push) Successful in 1m27s
Release / Docker / ui (push) Successful in 2m32s
Release / Gitea Release (push) Successful in 42s
All checks were successful
Release / Test backend (push) Successful in 41s
Release / Check ui (push) Successful in 1m41s
Release / Docker / caddy (push) Successful in 41s
Release / Docker / backend (push) Successful in 3m23s
Release / Docker / runner (push) Successful in 2m53s
Release / Upload source maps (push) Successful in 1m27s
Release / Docker / ui (push) Successful in 2m32s
Release / Gitea Release (push) Successful in 42s
When PREBUILT=1 the pre-built artifact is downloaded into ui/build/ but
.dockerignore excludes 'build', so Docker never sees it and /app/build
doesn't exist in the builder stage — causing the runtime COPY to fail.
Fix: rewrite ui/.dockerignore on the CI runner (grep -v '^build$') so the
pre-built directory is included in the Docker context.
Also in this commit:
- book page: gate EPUB download on isPro (UI upsell + server 403 guard)
- book page: chapter names default pattern changed to '{scene}'
This commit is contained in:
@@ -239,6 +239,11 @@ jobs:
|
|||||||
name: ui-build-injected
|
name: ui-build-injected
|
||||||
path: ui/build
|
path: ui/build
|
||||||
|
|
||||||
|
- name: Allow build/ into Docker context (override .dockerignore)
|
||||||
|
run: |
|
||||||
|
grep -v '^build$' ui/.dockerignore > ui/.dockerignore.tmp
|
||||||
|
mv ui/.dockerignore.tmp ui/.dockerignore
|
||||||
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import { error } from '@sveltejs/kit';
|
|||||||
import type { RequestHandler } from './$types';
|
import type { RequestHandler } from './$types';
|
||||||
import { backendFetch } from '$lib/server/scraper';
|
import { backendFetch } from '$lib/server/scraper';
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ params, url }) => {
|
export const GET: RequestHandler = async ({ params, url, locals }) => {
|
||||||
|
if (!locals.isPro) {
|
||||||
|
error(403, 'EPUB download requires a Pro subscription');
|
||||||
|
}
|
||||||
|
|
||||||
const { slug } = params;
|
const { slug } = params;
|
||||||
const from = url.searchParams.get('from');
|
const from = url.searchParams.get('from');
|
||||||
const to = url.searchParams.get('to');
|
const to = url.searchParams.get('to');
|
||||||
|
|||||||
@@ -64,8 +64,9 @@ export const load: PageServerLoad = async ({ params, locals }) => {
|
|||||||
lastChapter: null,
|
lastChapter: null,
|
||||||
userRating: 0,
|
userRating: 0,
|
||||||
ratingAvg: { avg: 0, count: 0 },
|
ratingAvg: { avg: 0, count: 0 },
|
||||||
isAdmin: locals.user?.role === 'admin',
|
isAdmin: locals.user?.role === 'admin',
|
||||||
isLoggedIn: !!locals.user,
|
isPro: locals.isPro,
|
||||||
|
isLoggedIn: !!locals.user,
|
||||||
currentUserId: locals.user?.id ?? '',
|
currentUserId: locals.user?.id ?? '',
|
||||||
scraping: true,
|
scraping: true,
|
||||||
taskId: body.task_id
|
taskId: body.task_id
|
||||||
|
|||||||
@@ -326,7 +326,7 @@
|
|||||||
let chapNamesPreview = $state<{ number: number; old_title: string; new_title: string; edited: string }[]>([]);
|
let chapNamesPreview = $state<{ number: number; old_title: string; new_title: string; edited: string }[]>([]);
|
||||||
let chapNamesApplying = $state(false);
|
let chapNamesApplying = $state(false);
|
||||||
let chapNamesResult = $state<'applied' | 'error' | ''>('');
|
let chapNamesResult = $state<'applied' | 'error' | ''>('');
|
||||||
let chapNamesPattern = $state('Chapter {n}: {scene}');
|
let chapNamesPattern = $state('{scene}');
|
||||||
let chapNamesBatchProgress = $state('');
|
let chapNamesBatchProgress = $state('');
|
||||||
let chapNamesBatchWarnings = $state<string[]>([]);
|
let chapNamesBatchWarnings = $state<string[]>([]);
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
const res = await fetch('/api/admin/text-gen/chapter-names', {
|
const res = await fetch('/api/admin/text-gen/chapter-names', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ slug, pattern: chapNamesPattern.trim() || 'Chapter {n}: {scene}' })
|
body: JSON.stringify({ slug, pattern: chapNamesPattern.trim() || '{scene}' })
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
chapNamesResult = 'error';
|
chapNamesResult = 'error';
|
||||||
@@ -835,13 +835,22 @@
|
|||||||
<p class="text-sm font-medium text-(--color-text)">Download</p>
|
<p class="text-sm font-medium text-(--color-text)">Download</p>
|
||||||
<p class="text-xs text-(--color-muted)">All {chapterList.length} chapters as EPUB</p>
|
<p class="text-xs text-(--color-muted)">All {chapterList.length} chapters as EPUB</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
{#if data.isPro}
|
||||||
href="/api/export/{book.slug}"
|
<a
|
||||||
download="{book.slug}.epub"
|
href="/api/export/{book.slug}"
|
||||||
class="px-3 py-1.5 rounded-lg bg-(--color-surface-2) border border-(--color-border) text-sm font-medium text-(--color-muted) hover:text-(--color-text) hover:border-zinc-500 transition-colors flex-shrink-0"
|
download="{book.slug}.epub"
|
||||||
>
|
class="px-3 py-1.5 rounded-lg bg-(--color-surface-2) border border-(--color-border) text-sm font-medium text-(--color-muted) hover:text-(--color-text) hover:border-zinc-500 transition-colors flex-shrink-0"
|
||||||
.epub
|
>
|
||||||
</a>
|
.epub
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<a
|
||||||
|
href="/profile"
|
||||||
|
class="px-3 py-1.5 rounded-lg bg-(--color-brand)/15 border border-(--color-brand)/30 text-sm font-medium text-(--color-brand) hover:bg-(--color-brand)/25 transition-colors flex-shrink-0"
|
||||||
|
>
|
||||||
|
Pro
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -1135,7 +1144,7 @@
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={chapNamesPattern}
|
bind:value={chapNamesPattern}
|
||||||
placeholder="Chapter {'{n}'}: {'{scene}'}"
|
placeholder="{'{scene}'}"
|
||||||
class="w-full px-2 py-1.5 rounded bg-(--color-surface-3) border border-(--color-border) text-(--color-text) text-xs focus:outline-none focus:border-(--color-brand)"
|
class="w-full px-2 py-1.5 rounded bg-(--color-surface-3) border border-(--color-border) text-(--color-text) text-xs focus:outline-none focus:border-(--color-brand)"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-3 flex-wrap">
|
<div class="flex items-center gap-3 flex-wrap">
|
||||||
|
|||||||
Reference in New Issue
Block a user