update: Updated for production

mack-mac
mackt 8 months ago
parent 7b810bda0e
commit 5f112164f2

@ -1,3 +1,3 @@
# 生产环境使用的变量
NEXT_PUBLIC_HOST = 'http://39.103.180.196:9012/'
NEXT_PUBLIC_HOST = 'http://api.indie.cn:9012'

@ -1,3 +0,0 @@
# 测试环境使用的变量
NEXT_PUBLIC_HOST = 'http://39.103.180.196:9012/'

73
.gitignore vendored

@ -1,36 +1,37 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
/dist

@ -0,0 +1,43 @@
# Install dependencies only when needed
FROM node:alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN yarn install --frozen-lockfile --registry=https://registry.npm.taobao.org
# Rebuild the source code only when needed
FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline --registry=https://registry.npm.taobao.org
# Production image, copy all the files and run next
FROM node:alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# You only need to copy next.config.js if you are NOT using the default configuration
# COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
USER nextjs
EXPOSE 3000
ENV PORT 3000
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry.
# ENV NEXT_TELEMETRY_DISABLED 1
CMD ["node_modules/.bin/next", "start"]

@ -1,22 +1,28 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// output: 'export',
distDir: 'dist',
async rewrites() {
return [
{
source: '/queyueapi/:path*',
destination: `${process.env.NEXT_PUBLIC_HOST}/:path*`,
destination: `http://api.indie.cn:9012/:path*`,
},
];
},
redirects: async () => {
return [
{
source: '/',
destination: '/vol/list/all',
permanent: true,
},
// {
// source: '/',
// destination: '/download',
// permanent: false,
// },
];
},
eslint: {
ignoreDuringBuilds: true,
ignoreBuildErrors: true,
},
};
export default nextConfig;

@ -5,8 +5,9 @@
"scripts": {
"dev": "next dev -p 3001",
"build": "next build",
"start": "next start -p 3001",
"lint": "eslint src --fix --ext .ts,.tsx,.js,.jsx,.mdx,.md,.json,.mjs --max-warnings 0"
"start": "next start -p 80",
"lint": "eslint src --fix --ext .ts,.tsx,.js,.jsx,.mdx,.md,.json,.mjs --max-warnings 0",
"pm2start": "pm2 start npm --name queryue-website -- run start"
},
"dependencies": {
"lodash": "^4.17.21",

@ -30,7 +30,7 @@ const options: NextAuthOptions = {
if (result.code === 200) {
return result.data;
} else {
console.log(`error ${result.code}/${result.messgae}`);
// console.log(`error ${result.code}/${result.messgae}`);
throw new Error(`error ${result.code}/${result.messgae}`);
}
} catch (error) {
@ -44,7 +44,7 @@ const options: NextAuthOptions = {
maxAge: 7 * 24 * 60 * 60,
},
pages: {
signIn: '/', // 重定向到 /login
signIn: '/download', // 重定向到 /login
},
// callbacks: {
// async jwt({ token, user }) {

@ -34,7 +34,7 @@ export default function Footer({ platform, iconUrl, qrCode }: DownloadQrcodeCard
{/* 二维码 */}
<div
className={`${styles.qrcodeContainer} ${showQrCode ? 'block' : 'hidden'} transition-opacity duration-500 opacity-100`}
className={`${styles.qrcodeContainer} ${showQrCode ? 'block' : 'hidden'} transition-opacity duration-500 opacity-100 z-2`}
>
{qrCode ? (
<Image

@ -24,7 +24,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
<Header />
<div>{children}</div>
<Footer />
<PlayerBar />
{/* <PlayerBar /> */}
</body>
</html>
);

@ -1,3 +1,7 @@
export default function Loading() {
return <div>Loading...</div>;
return (
<div className="w-[100vw] h-[100vh] flex justify-center items-center">
<div>loading...</div>
</div>
);
}

@ -1,3 +1,68 @@
export default function Home() {
return <main></main>;
}
'use client';
import Image from 'next/image';
import DownloadCard from '@/app/download/components/DownloadCard';
const qrCodeList: Array<DownloadQrcodeCard> = [
{
platform: 'APP Store',
iconUrl: '/img/download/logo_apple.svg',
qrCode: '',
},
{
platform: 'Android',
iconUrl: '/img/download/logo_android.svg',
qrCode: '/img/download/qrcode_android.svg',
},
];
export default function Download() {
return (
<main className="w-full flex flex-col items-center pb-[104px] font-normal">
{/* 首屏 */}
<div className="relative w-full h-min-[1000px] h-screen flex flex-col items-center pt-[30vh] bg-[url(/img/download/background_1.png)] bg-center bg-cover bg-no-repeat text-[#fff]">
{/* slogan */}
<div className="flex flex-col items-center mb-[35px]">
<Image width={410} height={50} src="/img/download/slogan_line1.svg" alt="为独立音乐,雀跃" />
<Image className="mt-[26px] mb-[26px]" width={9} height={24} src="/img/download/slogan_line2.svg" alt="/" />
<Image width={143} height={24} src="/img/download/slogan_line3.svg" alt="独立 不独于世" />
</div>
{/* APP二维码 */}
<div className="flex flex-row">
{qrCodeList.map(({ platform, iconUrl, qrCode }) => (
<DownloadCard key={platform} platform={platform} iconUrl={iconUrl} qrCode={qrCode} />
))}
</div>
</div>
{/* 第二屏 */}
<div className="relative w-full h-[1000px] h-screen pl-[45.7vw] flex flex-col bg-[url(/img/download/background_2.png)] bg-center bg-cover bg-no-repeat">
{/* 花体字-“我们回来了” */}
<Image className="mt-[16.67vh] mb-[50px]" width={306} height={86} src="/img/download/back.svg" alt="back" />
{/* 诗歌 */}
<p className="pl-[15px] text-[#000] text-[15px] leading-[28px] text-left">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
便
<br />
</p>
</div>
</main>
);
}

@ -3,7 +3,11 @@ import { apiSearchCategory } from '@/services';
export default async function Journal({ params }: { params: { category?: string; page?: number } }) {
const { category = 'all', page = 1 } = params;
const categoryList: Category[] = await apiSearchCategory();
const result = await apiSearchCategory();
let categoryList: Category[] = [];
if (result) {
categoryList = result;
}
const categoryInfo: Category | undefined = categoryList.find((item: Category) => item.nameEn === category);
return (

@ -3,7 +3,10 @@ import Link from 'next/link';
import { apiSearchCategory } from '@/services/server/journal';
const Category = async ({ current = '' }: { current?: string }) => {
const categoryList = await apiSearchCategory();
const result = await apiSearchCategory();
let categoryList: any = [];
if (result) categoryList = result;
categoryList.unshift({
id: 0,
nameCh: '全部',

@ -6,7 +6,7 @@ export default function ContributorCard({ nickName, avatar, contributorRole }: C
return (
<div className="flex flex-col items-center w-[178px] h-[260px] mt-[26px] pt-[36px] pb-[44px] rounded-[6px] bg-[#fff]">
<Avatar size={92} src={avatar} alt={`${nickName}-avatar`} />
<p className="w-full mt-[31px] mb-[13px] px-[20px] text-[18px] leading-[25px] text-[rgba(0,0,0,0.95)] text-center overflow-hidden whitespace-nowrap overflow-ellipsis truncate">
<p className="w-full mt-[31px] mb-[13px] px-[20px] text-[18px] leading-[25px] text-[rgba(0,0,0,0.95)] text-center overflow-hidden whitespace-nowrap truncate">
{nickName}
</p>
<p className="text-[15px] leading-[18px] text-[rgba(0,0,0,0.4)] text-center">{contributorRole}</p>

@ -1,34 +1,38 @@
export default function Footer() {
const agreementList = [
{
name: '服务条款',
url: '/',
},
{
name: '版权声明',
url: '/',
},
{
name: '许可协议',
url: '/',
},
];
return (
<footer className="absolute bottom-[40px] flex flex-col items-center w-full justify-center text-center text-[12px] leading-[16.8px]">
<div className="w-[1200px] mx-auto">
<p className="mt-[18px] mb-[12px] text-[rgba(0,0,0,0.4)]">
ICP2024190175-1 Shenzhen QueYue Culture Technology Co., Ltd.
</p>
<p className="text-[rgba(0,0,0,0.7)]">
{agreementList.map(({ name, url }) => (
<a className="mx-[4px]" href={url} key={name}>
{name}
</a>
))}
</p>
</div>
</footer>
);
}
export default function Footer() {
const agreementList = [
{
name: '知识产权说明',
url: 'http://cdn.indie.cn/html/agreement/intellectualPropertyDescription.html',
},
{
name: '注册协议',
url: 'http://cdn.indie.cn/html/agreement/registrationAgreement.html',
},
{
name: '隐私政策',
url: 'http://cdn.indie.cn/html/agreement/privacyPolicy.html',
},
{
name: '投诉指引',
url: 'http://cdn.indie.cn/html/agreement/complaintGuidelines.html',
},
];
return (
<footer className="absolute bottom-[40px] flex flex-col items-center w-full justify-center text-center text-[12px] leading-[16.8px]">
<div className="w-[1200px] mx-auto">
<p className="mt-[18px] mb-[12px] text-[rgba(0,0,0,0.4)]">
ICP2024190175-1 Shenzhen QueYue Culture Technology Co., Ltd.
</p>
<p className="text-[rgba(0,0,0,0.7)]">
{agreementList.map(({ name, url }) => (
<a className="mx-[4px] hover:text-theme" href={url} key={name} target="_blank">
{name}
</a>
))}
</p>
</div>
</footer>
);
}

@ -11,13 +11,13 @@ export default function Header() {
const pathName = usePathname();
const menuList = [
{
name: '首页',
path: '/',
},
// {
// name: '首页',
// path: '/',
// },
{
name: 'APP下载',
path: '/download',
path: '/',
},
{
name: '关于我们',
@ -48,12 +48,12 @@ export default function Header() {
))}
</ul>
<button
{/* <button
className="w-[74px] h-[36px] border-[#000] border-[1.5px] rounded-[60px] text-[17px] hover:bg-theme hover:border-theme hover:text-[#fff] "
onClick={handleLoginModalToggle}
>
</button>
</button> */}
</div>
{/* 登录框 */}

@ -9,7 +9,7 @@ export default function JournalItem({ title, image, totalCommentReply, journalNo
</Link>
<div className="flex flex-col justify-between h-full ml-[20px] py-[6px]">
<p className="w-[200px] text-[15px] leading-[21px] cursor-pointer hover:color-theme overflow-hidden whitespace-nowrap overflow-ellipsis truncate">
<p className="w-[200px] text-[15px] leading-[21px] cursor-pointer hover:color-theme overflow-hidden whitespace-nowrap truncate">
{title}
</p>
<p className="text-[13px] leading-[18.2px] cursor-pointer hover:color-theme">{`${totalCommentReply}人收藏`}</p>

@ -18,7 +18,7 @@ export default function JournalItem({
<JournalCard image={image} title={title} journalNo={journalNo} />
</Link>
{/* 摘要 */}
<p className="w-full mt-[15px] mb-[12px] text-[17px] leading-[23.8px] text-base hover:color-theme cursor-pointer overflow-hidden whitespace-nowrap overflow-ellipsis truncate">
<p className="w-full mt-[15px] mb-[12px] text-[17px] leading-[23.8px] text-base hover:color-theme cursor-pointer overflow-hidden whitespace-nowrap truncate">
{summary}
</p>
{/* 精选评论 */}
@ -32,7 +32,7 @@ export default function JournalItem({
</div>
)}
</div>
<p className="w-[270px] text-[14px] leading-[20px] text-[rgba(0,0,0,0.7)] hover:color-theme cursor-pointer overflow-hidden whitespace-nowrap overflow-ellipsis truncate">
<p className="w-[270px] text-[14px] leading-[20px] text-[rgba(0,0,0,0.7)] hover:color-theme cursor-pointer overflow-hidden whitespace-nowrap truncate">
{commentList[0].content}
</p>
</div>

@ -1,6 +1,6 @@
// client端请求
import { getCookie } from './cookie';
import { getApiUrl } from './helpers';
// import { getCookie } from './cookie';
// import { getApiUrl } from './helpers';
import { createFormBody } from './wrapper';
interface RequestOptions extends RequestInit {
@ -11,16 +11,15 @@ interface RequestOptions extends RequestInit {
// 发送数据请求
const request = async (url: string, config?: RequestOptions) => {
const method = config?.method || 'GET';
// const finalUrl = method === 'POST' ? `/queyueapi${url}` : `${process.env.NEXT_PUBLIC_HOST}/${url}`;
const finalUrl = method === 'POST' ? `${url}` : `${process.env.NEXT_PUBLIC_HOST}/${url}`;
const finalUrl = method === 'POST' ? `/queyueapi${url}` : `http://api.indie.cn:9012/${url}`;
const inital: RequestOptions = {
method: method,
body: null,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization:
'Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNzcyNjUzODM0MjYzNDY1OTg0Iiwic3ViIjoi6ZuA5LmQLWU5MnFQSHU2cSIsImlhdCI6MTcxMTU0MDgzOCwiYXZhdGFyIjoidXNlci9hdmF0YXIvZGVmYXVsdF8yMDI0MDIyN18yMzI5NDkuanBnIiwicm9sZXMiOiJ1c2VyIiwiZXhwIjoxNzEyMTQ1NjM4fQ.LqXi6ogm1jxK78-elx9vqNDQKXqzwQEoRRLpLj-PoGo',
Authorization: '',
// 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNzcyNjUzODM0MjYzNDY1OTg0Iiwic3ViIjoi6ZuA5LmQLWU5MnFQSHU2cSIsImlhdCI6MTcxMTU0MDgzOCwiYXZhdGFyIjoidXNlci9hdmF0YXIvZGVmYXVsdF8yMDI0MDIyN18yMzI5NDkuanBnIiwicm9sZXMiOiJ1c2VyIiwiZXhwIjoxNzEyMTQ1NjM4fQ.LqXi6ogm1jxK78-elx9vqNDQKXqzwQEoRRLpLj-PoGo',
},
credentials: 'include',
cache: 'no-cache',

@ -21,6 +21,6 @@
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "dist/types/**/*.ts"],
"exclude": ["node_modules"]
}

@ -26,7 +26,7 @@ export default defineConfig({
'switch-label-base': 'bg-gray-200 dark:bg-gray-800 switch-animation',
'switch-span-base': 'bg-white dark:bg-gray-300 switch-animation',
'text-theme': 'text-[#B44343]',
'text-overflow': 'overflow-hidden whitespace-nowrap overflow-ellipsis truncate',
'text-overflow': 'overflow-hidden whitespace-nowrap truncate',
'border-theme': 'border-[#B44343]',
'bg-theme': 'bg-[#B44343]',
},

Loading…
Cancel
Save