01270029
This commit is contained in:
parent
406fa8014f
commit
4d4f8f2cda
13
Dockerfile
13
Dockerfile
|
@ -85,7 +85,10 @@ EXPOSE 80
|
|||
CMD ["/usr/bin/entrypoint.sh"]
|
||||
|
||||
|
||||
# 使用 Nginx 的 Alpine 版本作为基础镜像
|
||||
FROM nginx:stable-alpine as nginx
|
||||
|
||||
# 替换 Alpine 的软件源为阿里云镜像
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
# 设置工作目录
|
||||
|
@ -94,9 +97,11 @@ WORKDIR /usr/share/nginx/html
|
|||
# 设置环境变量
|
||||
ENV NODE_ENV production
|
||||
|
||||
# 安装 envsubst 以支持环境变量替换
|
||||
RUN apk add --no-cache gettext
|
||||
# 安装 envsubst 和 inotify-tools
|
||||
RUN apk add --no-cache gettext inotify-tools
|
||||
|
||||
# 创建 /data/uploads 目录
|
||||
RUN mkdir -p /data/uploads
|
||||
|
||||
# 暴露 80 端口
|
||||
EXPOSE 80
|
||||
|
||||
EXPOSE 80
|
|
@ -31,7 +31,7 @@ export class AuthService {
|
|||
return { isValid: false, error: FileValidationErrorType.INVALID_URI };
|
||||
}
|
||||
const fileId = extractFileIdFromNginxUrl(params.originalUri);
|
||||
console.log(params.originalUri, fileId);
|
||||
console.log('auth', params.originalUri, fileId);
|
||||
const resource = await db.resource.findFirst({ where: { fileId } });
|
||||
|
||||
// 资源验证
|
||||
|
@ -170,13 +170,13 @@ export class AuthService {
|
|||
showname,
|
||||
department: deptId
|
||||
? {
|
||||
connect: { id: deptId },
|
||||
}
|
||||
connect: { id: deptId },
|
||||
}
|
||||
: undefined,
|
||||
domain: deptId
|
||||
? {
|
||||
connect: { id: deptId },
|
||||
}
|
||||
connect: { id: deptId },
|
||||
}
|
||||
: undefined,
|
||||
// domainId: data.deptId,
|
||||
meta: {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { db, PostState, PostType, VisitType } from '@nice/common';
|
||||
export async function updatePostViewCount(id: string, type: VisitType) {
|
||||
console.log('updatePostViewCount', type);
|
||||
const totalViews = await db.visit.aggregate({
|
||||
_sum: {
|
||||
views: true,
|
||||
|
|
|
@ -42,17 +42,18 @@ export function SendCard({ staff, termId }: SendCardProps) {
|
|||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h3 className="text-2xl font-semibold text-gray-700">
|
||||
{/* 修改后的职级显示部分 */}
|
||||
{staff.meta?.rank && (
|
||||
<span className="text-2xl font-bold text-primary tracking-wide mr-2">
|
||||
{staff.meta.rank}
|
||||
</span>
|
||||
)}
|
||||
<h3 className="text-2xl font-semibold text-primary">
|
||||
{staff?.showname || staff?.username}
|
||||
</h3>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip title="职级">
|
||||
<span className="inline-flex items-center px-4 py-1.5 text-sm font-medium bg-gradient-to-r from-blue-50 to-blue-100 text-primary rounded-full hover:from-blue-100 hover:to-blue-200 transition-colors shadow-sm">
|
||||
{staff.meta?.rank || '未设置职级'}
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Contact Information */}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useState, useCallback, useEffect, useMemo } from "react";
|
||||
import { useState, useCallback, useEffect} from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
import { SendCard } from "./SendCard";
|
||||
import { Spin, Empty, Input, Alert, Pagination } from "antd";
|
||||
import { api, useTerm } from "@nice/client";
|
||||
|
@ -9,7 +8,7 @@ import DepartmentSelect from "@web/src/components/models/department/department-s
|
|||
import debounce from "lodash/debounce";
|
||||
import { SearchOutlined } from "@ant-design/icons";
|
||||
import WriteHeader from "./WriteHeader";
|
||||
import { ObjectType, RoleName } from "@nice/common";
|
||||
import { RoleName, staffDetailSelect } from "@nice/common";
|
||||
|
||||
export default function WriteLetterPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
@ -23,18 +22,17 @@ export default function WriteLetterPage() {
|
|||
api.rolemap.getStaffIdsByRoleNames.useQuery({
|
||||
roleNames: [RoleName.Leader, RoleName.Organization],
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
const { data, isLoading, error } =
|
||||
api.staff.findManyWithPagination.useQuery(
|
||||
{
|
||||
page: currentPage,
|
||||
pageSize,
|
||||
select: staffDetailSelect,
|
||||
where: {
|
||||
id: enabledStaffIds
|
||||
? {
|
||||
in: enabledStaffIds,
|
||||
}
|
||||
in: enabledStaffIds,
|
||||
}
|
||||
: undefined,
|
||||
deptId: selectedDept,
|
||||
OR: [
|
||||
|
|
|
@ -3,6 +3,7 @@ import { message, Progress, Spin, theme } from "antd";
|
|||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useTusUpload } from "@web/src/hooks/useTusUpload";
|
||||
import { Avatar } from "antd/lib";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export interface AvatarUploaderProps {
|
||||
value?: string;
|
||||
|
@ -67,6 +68,7 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
|
|||
url: result.url,
|
||||
compressedUrl: result.compressedUrl,
|
||||
}));
|
||||
|
||||
setUrl(result.url);
|
||||
setPreviewUrl(result.compressedUrl);
|
||||
// 直接使用 result 中的最新值
|
||||
|
@ -78,15 +80,13 @@ const AvatarUploader: React.FC<AvatarUploaderProps> = ({
|
|||
file?.fileKey
|
||||
);
|
||||
});
|
||||
// await new Promise((resolve) => setTimeout(resolve, 5000)); // 方法1:使用 await 暂停执行
|
||||
// 使用 resolved 的最新值调用 onChange
|
||||
// 强制刷新 Avatar 组件
|
||||
setAvatarKey((prev) => prev + 1); // 修改 key 强制重新挂载
|
||||
onChange?.(uploadedUrl);
|
||||
message.success("头像上传成功");
|
||||
console.log(uploadedUrl)
|
||||
toast.success("头像上传成功");
|
||||
} catch (error) {
|
||||
console.error("上传错误:", error);
|
||||
message.error("头像上传失败");
|
||||
toast.error("头像上传失败");
|
||||
setFile((prev) => ({ ...prev!, status: "error" }));
|
||||
} finally {
|
||||
setUploading(false);
|
||||
|
|
|
@ -9,7 +9,7 @@ export default function Content() {
|
|||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const contentWrapperRef = useRef(null);
|
||||
const [shouldCollapse, setShouldCollapse] = useState(false);
|
||||
|
||||
console.log(post)
|
||||
useEffect(() => {
|
||||
if (contentWrapperRef.current) {
|
||||
const shouldCollapse = contentWrapperRef.current.scrollHeight > 150;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { PostDto } from "@nice/common";
|
|||
import { env } from "@web/src/env";
|
||||
import { getFileIcon } from "./utils";
|
||||
import { formatFileSize, getCompressedImageUrl } from '@nice/utils';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
||||
export default function PostResources({ post }: { post: PostDto }) {
|
||||
|
@ -29,7 +30,6 @@ export default function PostResources({ post }: { post: PostDto }) {
|
|||
|
||||
const imageResources = resources.filter((res) => res.isImage);
|
||||
const fileResources = resources.filter((res) => !res.isImage);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{imageResources.length > 0 && (
|
||||
|
@ -44,12 +44,9 @@ export default function PostResources({ post }: { post: PostDto }) {
|
|||
lg={6}
|
||||
xl={4}
|
||||
className="relative">
|
||||
<div className="relative aspect-square rounded-lg overflow-hidden shadow-md hover:shadow-lg transition-shadow duration-300 bg-gray-100">
|
||||
<div className="relative aspect-square rounded-lg overflow-hidden bg-gray-100">
|
||||
<div className="w-full h-full">
|
||||
<Image
|
||||
onError={(e) => {
|
||||
console.log(e)
|
||||
}}
|
||||
src={resource.url}
|
||||
alt={resource.title}
|
||||
preview={{
|
||||
|
|
|
@ -45,7 +45,6 @@ export function LetterFormProvider({
|
|||
|
||||
const onSubmit = async (data: LetterFormData) => {
|
||||
try {
|
||||
console.log("data", data);
|
||||
const receivers = data?.receivers;
|
||||
const terms = data?.terms;
|
||||
delete data.receivers;
|
||||
|
|
|
@ -74,7 +74,8 @@ export function useTusUpload() {
|
|||
[fileKey]: progress,
|
||||
}));
|
||||
},
|
||||
onSuccess: async () => {
|
||||
onSuccess: async (payload) => {
|
||||
|
||||
try {
|
||||
if (upload.url) {
|
||||
const fileId = getFileId(upload.url);
|
||||
|
|
|
@ -65,10 +65,9 @@ server {
|
|||
sendfile on;
|
||||
tcp_nopush on;
|
||||
# 异步IO
|
||||
aio on;
|
||||
aio off;
|
||||
# 直接IO,提高大文件传输效率
|
||||
directio 512;
|
||||
|
||||
directio off;
|
||||
# 文件访问认证
|
||||
# 通过内部认证服务验证
|
||||
auth_request /auth-file;
|
||||
|
@ -77,9 +76,12 @@ server {
|
|||
auth_request_set $auth_user_id $upstream_http_x_user_id;
|
||||
auth_request_set $auth_resource_type $upstream_http_x_resource_type;
|
||||
# 不缓存
|
||||
expires 0;
|
||||
# expires 0;
|
||||
# 私有缓存,禁止转换
|
||||
add_header Cache-Control "private, no-transform";
|
||||
# add_header Cache-Control "private, no-transform";
|
||||
open_file_cache off; # 禁用文件缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate"; # 禁用浏览器缓存
|
||||
expires -1; # 立即过期
|
||||
# 添加用户和资源类型头
|
||||
add_header X-User-Id $auth_user_id;
|
||||
add_header X-Resource-Type $auth_resource_type;
|
||||
|
|
|
@ -68,7 +68,6 @@ server {
|
|||
aio on;
|
||||
# 直接IO,提高大文件传输效率
|
||||
directio 512;
|
||||
|
||||
# 文件访问认证
|
||||
# 通过内部认证服务验证
|
||||
auth_request /auth-file;
|
||||
|
@ -80,6 +79,8 @@ server {
|
|||
expires 0;
|
||||
# 私有缓存,禁止转换
|
||||
add_header Cache-Control "private, no-transform";
|
||||
|
||||
expires -1; # 立即过期
|
||||
# 添加用户和资源类型头
|
||||
add_header X-User-Id $auth_user_id;
|
||||
add_header X-Resource-Type $auth_resource_type;
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
import { Prisma } from "@prisma/client";
|
||||
|
||||
export const staffDetailSelect: Prisma.StaffSelect = {
|
||||
id: true,
|
||||
username: true,
|
||||
department: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true
|
||||
}
|
||||
},
|
||||
showname: true,
|
||||
phoneNumber: true,
|
||||
deptId: true,
|
||||
domain: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true
|
||||
}
|
||||
},
|
||||
domainId: true,
|
||||
meta: true
|
||||
}
|
||||
export const postDetailSelect: Prisma.PostSelect = {
|
||||
id: true,
|
||||
type: true,
|
||||
|
|
Loading…
Reference in New Issue