Skip to content

Commit

Permalink
Merge pull request #30 from besscroft/dev
Browse files Browse the repository at this point in the history
v0.7.2
  • Loading branch information
besscroft authored May 22, 2024
2 parents d60289c + edbc44d commit 4af4fcd
Show file tree
Hide file tree
Showing 27 changed files with 537 additions and 107 deletions.
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ COPY package.json pnpm-lock.yaml* .npmrc ./

RUN corepack enable pnpm && pnpm i --frozen-lockfile

FROM base AS runner-base

RUN apk add --no-cache libc6-compat

WORKDIR /app

RUN corepack enable pnpm && pnpm add prisma @prisma/client

FROM base AS builder

WORKDIR /app
Expand All @@ -29,6 +37,7 @@ ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=runner-base /app/node_modules ./node_modules
COPY --from=builder /app/public ./public
COPY ./prisma ./prisma
COPY ./script.sh ./script.sh
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ PicImpact

### 如何部署

你可以点击下面的按钮来一键部署到 Vercel,然后将 `Build Command` `pnpm run build:vercel`,也可以 Fork 项目后手动部署到任何支持的平台。
你可以点击下面的按钮来一键部署到 Vercel,然后将 `Build Command` 设置为 `pnpm run build:vercel`,也可以 Fork 项目后手动部署到任何支持的平台。

<a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fbesscroft%2FPicImpact&env=DATABASE_URL,AUTH_SECRET"><img src="https://vercel.com/button" alt="Deploy with Vercel"/></a>

Expand All @@ -26,6 +26,8 @@ PicImpact
默认账号:`[email protected]`,默认密码:`666666`**登录后请先去设置里面修改密码!**

> 部署就是这么简单,只需要您准备一个干净的数据库就行!
> 除了容器化部署方式外,其它的部署方式都需要执行 `pnpm run prisma:deploy` 来完成 prisma 迁移。
> 如果是 Vercel 部署,直接将 `Build Command` 设置为 `pnpm run build:vercel` 即可。
>
> 不过,不支持 ALL Edge 运行时,毕竟 Vercel 就只给了 Edge 100M 内存,太小了...当然,Node.js 运行时是完美支持的~
Expand All @@ -42,7 +44,7 @@ docker run -d --name picimpact \
```

```yaml
version: '3'
name: pic
services:
picimpact:
image: besscroft/picimpact:latest
Expand Down
26 changes: 26 additions & 0 deletions app/(default)/label/[...tag]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Masonry from '~/components/Masonry'
import { fetchClientImagesListByLabel, fetchClientImagesPageTotalByLabel } from '~/server/lib/query'
import { ImageHandleProps } from '~/types'

export default function Label({ params }: { params: { tag: string } }) {
const getData = async (pageNum: number, tag: string) => {
'use server'
return await fetchClientImagesListByLabel(pageNum, tag)
}

const getPageTotal = async (tag: string) => {
'use server'
return await fetchClientImagesPageTotalByLabel(tag)
}

const props: ImageHandleProps = {
handle: getData,
args: `getImages-client-label`,
tag: `${decodeURIComponent(params.tag)}`,
totalHandle: getPageTotal
}

return (
<Masonry {...props} />
)
}
50 changes: 25 additions & 25 deletions app/admin/settings/backup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import { Card, CardBody } from '@nextui-org/react'
import React, { useState } from 'react'
import { CloudUploadOutlined, CloudDownloadOutlined } from '@ant-design/icons'
import type { UploadProps } from 'antd'
import { Button, Upload } from 'antd'
// import type { UploadProps } from 'antd'
// import { Button, Upload } from 'antd'
import { toast } from 'sonner'
import dayjs from 'dayjs'

Expand Down Expand Up @@ -92,33 +92,33 @@ export default function Backup() {
option.onSuccess(option.file)
}

const picimpactProps: UploadProps = {
className: '!w-full sm:!w-64',
name: 'file',
maxCount: 1,
multiple: false,
showUploadList: false,
customRequest: async (file) => await onPicImpactRequestUpload(file),
};
// const picimpactProps: UploadProps = {
// className: '!w-full sm:!w-64',
// name: 'file',
// maxCount: 1,
// multiple: false,
// showUploadList: false,
// customRequest: async (file) => await onPicImpactRequestUpload(file),
// };

return (
<Card className="flex-1" shadow="sm">
<CardBody className="space-y-2">
<Button
className="!w-full sm:!w-64"
icon={<CloudDownloadOutlined />}
loading={backupLoading}
onClick={() => backup()}
aria-label="备份"
>备份</Button>
<Upload {...picimpactProps}>
<Button
className="!w-full sm:!w-64 !block"
icon={<CloudUploadOutlined />}
loading={restorePicImpactLoading}
aria-label="选择备份文件(本机迁移)"
>选择备份文件(本机迁移)</Button>
</Upload>
{/*<Button*/}
{/* className="!w-full sm:!w-64"*/}
{/* icon={<CloudDownloadOutlined />}*/}
{/* loading={backupLoading}*/}
{/* onClick={() => backup()}*/}
{/* aria-label="备份"*/}
{/*>备份</Button>*/}
{/*<Upload {...picimpactProps}>*/}
{/* <Button*/}
{/* className="!w-full sm:!w-64 !block"*/}
{/* icon={<CloudUploadOutlined />}*/}
{/* loading={restorePicImpactLoading}*/}
{/* aria-label="选择备份文件(本机迁移)"*/}
{/* >选择备份文件(本机迁移)</Button>*/}
{/*</Upload>*/}
<p>如果您在线上环境,请确保您的数据库单次会话时长以及事务的支持,否则会还原数据失败!</p>
</CardBody>
</Card>
Expand Down
2 changes: 1 addition & 1 deletion app/admin/tag/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default async function List() {
<CardHeader className="justify-between">
<div className="flex gap-5">
<div className="flex flex-col gap-1 items-start justify-center">
<h4 className="text-small font-semibold leading-none text-default-600 select-none">标签管理</h4>
<h4 className="text-small font-semibold leading-none text-default-600 select-none">相册管理</h4>
</div>
</div>
<div className="flex items-center space-x-2">
Expand Down
2 changes: 1 addition & 1 deletion components/Masonry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import MasonryItem from '~/components/MasonryItem'
export default function Masonry(props : Readonly<ImageHandleProps>) {
const { data: pageTotal } = useSWRPageTotalHook(props)
const { data, error, isLoading, isValidating, size, setSize } = useSWRInfinite((index) => {
return [`client--${index}-${props.tag}`, index]
return [`client-${props.args}-${index}-${props.tag}`, index]
},
([_, index]) => {
return props.handle(index + 1, props.tag)
Expand Down
19 changes: 18 additions & 1 deletion components/MasonryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import {
} from '~/components/ui/Dialog'
import { useButtonStore } from '~/app/providers/button-store-Providers'
import { ImageType } from '~/types'
import { Image, Tabs, Tab, Card, CardHeader, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Button } from '@nextui-org/react'
import { Image, Tabs, Tab, Card, CardHeader, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Button, Chip } from '@nextui-org/react'
import { Aperture, Camera, Image as ImageIcon, Languages, CalendarDays, X, SunMedium, MoonStar } from 'lucide-react'
import * as React from 'react'
import { useTheme } from 'next-themes'
import { useRouter } from 'next-nprogress-bar'

export default function MasonryItem() {
const router = useRouter()
const { MasonryView, MasonryViewData, setMasonryView, setMasonryViewData } = useButtonStore(
(state) => state,
)
Expand Down Expand Up @@ -109,6 +111,21 @@ export default function MasonryItem() {
<h4 className="font-bold text-large">{MasonryViewData?.exif?.data_time || 'N&A'}</h4>
</CardHeader>
</Card>
{MasonryViewData?.labels &&
<div className="space-x-1">
{MasonryViewData?.labels.map((tag: string) => (
<Chip
key={tag}
variant="bordered"
className="cursor-pointer select-none"
onClick={() => {
setMasonryView(false)
router.push(`/label/${tag}`)
}}
>{tag}</Chip>
))}
</div>
}
</div>
</Tab>
<Tab
Expand Down
4 changes: 2 additions & 2 deletions components/admin/dashboard/TagTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export default function TagTable(props: Readonly<DataProps>) {
<span className="text-xl font-semibold">{props.data?.showTotal || 0}</span>
</CardBody>
</Card>
<Table aria-label="每个标签对应的数量">
<Table aria-label="每个相册对应的数量">
<TableHeader>
<TableColumn>标签</TableColumn>
<TableColumn>相册</TableColumn>
<TableColumn>数量/张</TableColumn>
<TableColumn>显示/张</TableColumn>
</TableHeader>
Expand Down
8 changes: 8 additions & 0 deletions components/admin/list/ImageEditSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useSWRInfiniteServerHook } from '~/hooks/useSWRInfiniteServerHook'
import { Button, cn, Input, Switch, Textarea } from '@nextui-org/react'
import React, { useState } from 'react'
import { toast } from 'sonner'
import { TagInput } from '@douyinfe/semi-ui'

export default function ImageEditSheet(props : Readonly<ImageServerHandleProps & { pageNum: number } & { tag: string }>) {
const { pageNum, tag, ...restProps } = props
Expand Down Expand Up @@ -140,6 +141,13 @@ export default function ImageEditSheet(props : Readonly<ImageServerHandleProps &
label="排序"
placeholder="0"
/>
<TagInput
value={image.labels}
placeholder='请输入图片索引标签,如:原神,不要输入特殊字符。'
addOnBlur={true}
allowDuplicates={false}
onChange={(value) => setImageEditData({ ...image, labels: value })}
/>
<Switch
isSelected={image?.show === 0}
value={image?.show === 0 ? 'true' : 'false'}
Expand Down
42 changes: 42 additions & 0 deletions components/admin/list/ImageHelpSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client'

import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '~/components/ui/Sheet'
import { useButtonStore } from '~/app/providers/button-store-Providers'

export default function ImageHelpSheet() {
const { imageHelp, setImageHelp } = useButtonStore(
(state) => state,
)

return (
<Sheet
defaultOpen={false}
open={imageHelp}
onOpenChange={(open: boolean) => {
if (!open) {
setImageHelp(false)
}
}}
modal={false}
>
<SheetContent side="left">
<SheetHeader>
<SheetTitle>帮助</SheetTitle>
<SheetDescription className="space-y-2">
<p>
您在当前页面可以维护图片的数据。
</p>
<p>
您可以为每一张图片打上标签,但请注意不要用特殊字符。
为了兼容 SSR 场景,通过路由来获取的参数,如果有特殊字符可能会没法正确访问数据。
您可以通过点击图片标签,来访问所有包含该标签的图片。
</p>
<p>
注意,如果您将⌈图片⌋设置为⌈禁用状态⌋,那么未登录时无法以任何方式获取该图片的信息,但不影响通过链接访问图片,因为这个权限由存储方管理。
</p>
</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>
)
}
9 changes: 8 additions & 1 deletion components/admin/list/ImageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '~/components/ui/Sheet'
import { useButtonStore } from '~/app/providers/button-store-Providers'
import { ImageType } from '~/types'
import { cn, Input, Switch, Textarea, Image } from '@nextui-org/react'
import { cn, Input, Switch, Textarea, Image, Chip } from '@nextui-org/react'
import React from 'react'

export default function ImageView() {
Expand Down Expand Up @@ -32,6 +32,13 @@ export default function ImageView() {
src={imageViewData.url}
alt={imageViewData.detail}
/>
{imageViewData?.labels &&
<div className="space-x-1">
{imageViewData?.labels.map((tag: string) => (
<Chip key={tag} variant="bordered">{tag}</Chip>
))}
</div>
}
<Textarea
isReadOnly
value={imageViewData?.detail}
Expand Down
31 changes: 21 additions & 10 deletions components/admin/list/ListProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ import {
Badge,
Spinner
} from '@nextui-org/react'
import { ArrowDown10, Pencil, Trash, Eye, EyeOff, ScanSearch } from 'lucide-react'
import { ArrowDown10, Pencil, Trash, Eye, EyeOff, ScanSearch, CircleHelp } from 'lucide-react'
import { toast } from 'sonner'
import { useButtonStore } from '~/app/providers/button-store-Providers'
import ImageEditSheet from '~/components/admin/list/ImageEditSheet'
import ImageView from '~/components/admin/list/ImageView'
import { fetcher } from '~/utils/fetcher'
import useSWR from 'swr'
import { motion } from 'framer-motion'
import ImageHelpSheet from '~/components/admin/list/ImageHelpSheet'

export default function ListProps(props : Readonly<ImageServerHandleProps>) {
const [pageNum, setPageNum] = useState(1)
Expand All @@ -47,7 +48,7 @@ export default function ListProps(props : Readonly<ImageServerHandleProps>) {
const [deleteLoading, setDeleteLoading] = useState(false)
const [updateShowLoading, setUpdateShowLoading] = useState(false)
const [updateShowId, setUpdateShowId] = useState(0)
const { setImageEdit, setImageEditData, setImageView, setImageViewData } = useButtonStore(
const { setImageEdit, setImageEditData, setImageView, setImageViewData, setImageHelp } = useButtonStore(
(state) => state,
)
const { data: tags, isLoading: tagsLoading } = useSWR('/api/v1/get-tags', fetcher)
Expand Down Expand Up @@ -107,8 +108,8 @@ export default function ListProps(props : Readonly<ImageServerHandleProps>) {
<CardHeader className="justify-between space-x-2">
<div className="flex items-center justify-center w-full sm:w-64 md:w-80">
<Select
label="标签"
placeholder="请选择标签"
label="相册"
placeholder="请选择相册"
className="min-w-xs"
size="sm"
isLoading={tagsLoading}
Expand Down Expand Up @@ -136,6 +137,15 @@ export default function ListProps(props : Readonly<ImageServerHandleProps>) {
</Select>
</div>
<div className="flex items-center space-x-1">
<Button
isIconOnly
size="sm"
color="warning"
aria-label="帮助"
onClick={() => setImageHelp(true)}
>
<CircleHelp />
</Button>
<Button
color="primary"
radius="full"
Expand Down Expand Up @@ -172,25 +182,25 @@ export default function ListProps(props : Readonly<ImageServerHandleProps>) {
<Badge content={image.tag_values.split(",").length} color="primary">
<Popover placement="top" shadow="sm">
<PopoverTrigger className="cursor-pointer">
<Chip variant="shadow" className="flex-1" aria-label="标签">{image.tag_names.length > 8 ? image.tag_names.substring(0, 8) + '...' : image.tag_names}</Chip>
<Chip variant="shadow" className="flex-1" aria-label="相册">{image.tag_names.length > 8 ? image.tag_names.substring(0, 8) + '...' : image.tag_names}</Chip>
</PopoverTrigger>
<PopoverContent>
<div className="px-1 py-2 select-none">
<div className="text-small font-bold">标签</div>
<div className="text-tiny">图片标签,在对应的路由上显示</div>
<div className="text-small font-bold">相册</div>
<div className="text-tiny">图片在对应的相册上显示</div>
</div>
</PopoverContent>
</Popover>
</Badge>
:
<Popover placement="top" shadow="sm">
<PopoverTrigger className="cursor-pointer">
<Chip variant="shadow" className="flex-1" aria-label="标签">{image.tag_names}</Chip>
<Chip variant="shadow" className="flex-1" aria-label="相册">{image.tag_names}</Chip>
</PopoverTrigger>
<PopoverContent>
<div className="px-1 py-2 select-none">
<div className="text-small font-bold">标签</div>
<div className="text-tiny">图片标签,在对应的路由上显示</div>
<div className="text-small font-bold">相册</div>
<div className="text-tiny">图片在对应的相册上显示</div>
</div>
</PopoverContent>
</Popover>
Expand Down Expand Up @@ -333,6 +343,7 @@ export default function ListProps(props : Readonly<ImageServerHandleProps>) {
</Modal>
<ImageEditSheet {...{...props, pageNum, tag}} />
<ImageView />
<ImageHelpSheet />
</div>
)
}
Loading

0 comments on commit 4af4fcd

Please sign in to comment.