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. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies # dependencies
/node_modules /node_modules
/.pnp /.pnp
.pnp.js .pnp.js
.yarn/install-state.gz .yarn/install-state.gz
# testing # testing
/coverage /coverage
# next.js # next.js
/.next/ /.next/
/out/ /out/
# production # production
/build /build
# misc # misc
.DS_Store .DS_Store
*.pem *.pem
# debug # debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
# local env files # local env files
.env*.local .env*.local
# vercel # vercel
.vercel .vercel
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts 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} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
// output: 'export',
distDir: 'dist',
async rewrites() { async rewrites() {
return [ return [
{ {
source: '/queyueapi/:path*', source: '/queyueapi/:path*',
destination: `${process.env.NEXT_PUBLIC_HOST}/:path*`, destination: `http://api.indie.cn:9012/:path*`,
}, },
]; ];
}, },
redirects: async () => { redirects: async () => {
return [ return [
{ // {
source: '/', // source: '/',
destination: '/vol/list/all', // destination: '/download',
permanent: true, // permanent: false,
}, // },
]; ];
}, },
eslint: {
ignoreDuringBuilds: true,
ignoreBuildErrors: true,
},
}; };
export default nextConfig; export default nextConfig;

@ -5,8 +5,9 @@
"scripts": { "scripts": {
"dev": "next dev -p 3001", "dev": "next dev -p 3001",
"build": "next build", "build": "next build",
"start": "next start -p 3001", "start": "next start -p 80",
"lint": "eslint src --fix --ext .ts,.tsx,.js,.jsx,.mdx,.md,.json,.mjs --max-warnings 0" "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": { "dependencies": {
"lodash": "^4.17.21", "lodash": "^4.17.21",

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

@ -34,7 +34,7 @@ export default function Footer({ platform, iconUrl, qrCode }: DownloadQrcodeCard
{/* 二维码 */} {/* 二维码 */}
<div <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 ? ( {qrCode ? (
<Image <Image

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

@ -1,3 +1,7 @@
export default function Loading() { 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() { 'use client';
return <main></main>;
} 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 } }) { export default async function Journal({ params }: { params: { category?: string; page?: number } }) {
const { category = 'all', page = 1 } = params; 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); const categoryInfo: Category | undefined = categoryList.find((item: Category) => item.nameEn === category);
return ( return (

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

@ -6,7 +6,7 @@ export default function ContributorCard({ nickName, avatar, contributorRole }: C
return ( return (
<div className="flex flex-col items-center w-[178px] h-[260px] mt-[26px] pt-[36px] pb-[44px] rounded-[6px] bg-[#fff]"> <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`} /> <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} {nickName}
</p> </p>
<p className="text-[15px] leading-[18px] text-[rgba(0,0,0,0.4)] text-center">{contributorRole}</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() { export default function Footer() {
const agreementList = [ const agreementList = [
{ {
name: '服务条款', name: '知识产权说明',
url: '/', url: 'http://cdn.indie.cn/html/agreement/intellectualPropertyDescription.html',
}, },
{ {
name: '版权声明', name: '注册协议',
url: '/', url: 'http://cdn.indie.cn/html/agreement/registrationAgreement.html',
}, },
{ {
name: '许可协议', name: '隐私政策',
url: '/', url: 'http://cdn.indie.cn/html/agreement/privacyPolicy.html',
}, },
]; {
name: '投诉指引',
return ( url: 'http://cdn.indie.cn/html/agreement/complaintGuidelines.html',
<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. return (
</p> <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="text-[rgba(0,0,0,0.7)]"> <p className="mt-[18px] mb-[12px] text-[rgba(0,0,0,0.4)]">
{agreementList.map(({ name, url }) => ( ICP2024190175-1 Shenzhen QueYue Culture Technology Co., Ltd.
<a className="mx-[4px]" href={url} key={name}> </p>
{name}
</a> <p className="text-[rgba(0,0,0,0.7)]">
))} {agreementList.map(({ name, url }) => (
</p> <a className="mx-[4px] hover:text-theme" href={url} key={name} target="_blank">
</div> {name}
</footer> </a>
); ))}
} </p>
</div>
</footer>
);
}

@ -11,13 +11,13 @@ export default function Header() {
const pathName = usePathname(); const pathName = usePathname();
const menuList = [ const menuList = [
{ // {
name: '首页', // name: '首页',
path: '/', // path: '/',
}, // },
{ {
name: 'APP下载', name: 'APP下载',
path: '/download', path: '/',
}, },
{ {
name: '关于我们', name: '关于我们',
@ -48,12 +48,12 @@ export default function Header() {
))} ))}
</ul> </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] " 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} onClick={handleLoginModalToggle}
> >
</button> </button> */}
</div> </div>
{/* 登录框 */} {/* 登录框 */}

@ -9,7 +9,7 @@ export default function JournalItem({ title, image, totalCommentReply, journalNo
</Link> </Link>
<div className="flex flex-col justify-between h-full ml-[20px] py-[6px]"> <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} {title}
</p> </p>
<p className="text-[13px] leading-[18.2px] cursor-pointer hover:color-theme">{`${totalCommentReply}人收藏`}</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} /> <JournalCard image={image} title={title} journalNo={journalNo} />
</Link> </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} {summary}
</p> </p>
{/* 精选评论 */} {/* 精选评论 */}
@ -32,7 +32,7 @@ export default function JournalItem({
</div> </div>
)} )}
</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} {commentList[0].content}
</p> </p>
</div> </div>

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

@ -21,6 +21,6 @@
"@/*": ["./src/*"] "@/*": ["./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"] "exclude": ["node_modules"]
} }

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

Loading…
Cancel
Save