From 7fdb5ced21c7a616d0e7a13d1327e68c3a57fc34 Mon Sep 17 00:00:00 2001 From: Ruislan Date: Wed, 18 Oct 2023 16:48:52 +0800 Subject: [PATCH] refactor admin controllers --- README.md | 9 +- ktap-server/package.json | 4 +- ktap-server/src/models/app.js | 118 +++++++- ktap-server/src/models/review.js | 9 + ktap-server/src/routes/admin/apps.js | 283 +++++++++--------- ktap-server/yarn.lock | 66 ++-- ktap-ui-web/src/assets/css/rdp.css | 5 +- ktap-ui-web/src/components/rdpicker.jsx | 2 +- .../admin-panel/apps/app-detail-info.jsx | 2 +- .../admin-panel/apps/app-detail-languages.jsx | 6 +- .../admin-panel/apps/app-detail-tags.jsx | 6 +- .../src/pages/apps/tab-details-languages.jsx | 11 +- ktap-ui-web/src/pages/settings/profile.jsx | 4 +- .../src/pages/users/user-achievements.jsx | 2 +- 14 files changed, 326 insertions(+), 201 deletions(-) diff --git a/README.md b/README.md index ccb820a..cf52f12 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,15 @@ KTap是一个时尚的游戏社区平台。 - + - + @@ -105,13 +105,14 @@ SQLite仅仅作为开发、演示和独立小规模运营是非常棒的,甚 ## 接下来 -* 增加专题专栏(例如节日专题,打折专题,新品专题等等) +接下来可能会研发的功能... + * 增加 App 活动事件(线上或者线下活动) * 增加组织管理(组织所有人自己管理自己的组织) +* 增加专题专栏(例如节日专题,打折专题,新品专题等等) * 增加商城和充值(余额购买虚拟或者实体商品) * 增加搜索引擎,改进搜索、相关性算法 * 数据库支持MySQL或PG -* 持续重构和优化 ## 致敬 diff --git a/ktap-server/package.json b/ktap-server/package.json index feb1e6d..0fffe38 100644 --- a/ktap-server/package.json +++ b/ktap-server/package.json @@ -20,7 +20,7 @@ "@fastify/helmet": "^11.1.1", "@fastify/jwt": "^7.2.2", "@fastify/multipart": "^8.0.0", - "@fastify/sensible": "^5.3.0", + "@fastify/sensible": "^5.4.0", "@fastify/static": "^6.11.2", "@node-rs/jieba": "^1.7.2", "@prisma/client": "^5.4.2", @@ -28,7 +28,7 @@ "bcrypt": "^5.1.0", "cheerio": "^1.0.0-rc.12", "dotenv": "^16.0.3", - "fastify": "^4.24.0", + "fastify": "^4.24.2", "ms": "^2.1.3", "node-cron": "^3.0.2", "nodemailer": "^6.9.1", diff --git a/ktap-server/src/models/app.js b/ktap-server/src/models/app.js index 2cf87cc..dc46b9a 100644 --- a/ktap-server/src/models/app.js +++ b/ktap-server/src/models/app.js @@ -1,6 +1,6 @@ 'use strict'; -import { AppMedia } from '../constants.js'; +import { AppMedia, TagCategory } from '../constants.js'; const app = async (fastify, opts, next) => { fastify.decorate('app', { @@ -86,6 +86,122 @@ const app = async (fastify, opts, next) => { // 用户可能输入了类型或者功能相同的词,要排除掉,这些词是不需要标记的 if (theTag.category === TagCategory.normal) await fastify.db.appUserTagRef.upsert({ create: { appId, userId, tagId: theTag.id }, update: {}, where: { appId_userId_tagId: { userId, appId, tagId: theTag.id } } }); }, + + // these actions for admin + async createApp({ name }) { + const newApp = await fastify.db.app.create({ + data: { + name, + isVisible: false, // 初始为不可见,操作完成之后再可见 + media: { + create: [ + { type: AppMedia.type.image, usage: AppMedia.usage.head, image: 'https://placehold.co/460x215/png', thumbnail: 'https://placehold.co/292x136/png' }, + { type: AppMedia.type.image, usage: AppMedia.usage.landscape, image: 'https://placehold.co/2560x1440/png', thumbnail: 'https://placehold.co/616x353/png' }, + { type: AppMedia.type.image, usage: AppMedia.usage.portrait, image: 'https://placehold.co/1200x1600/png', thumbnail: 'https://placehold.co/374x448/png' }, + { type: AppMedia.type.image, usage: AppMedia.usage.logo, image: 'https://placehold.co/400x400/png', thumbnail: 'https://placehold.co/128x128/png' } + ] + } + }, + }); + return newApp; + }, + async updateAppBasis({ appId, isVisible, score }) { + await fastify.db.app.update({ + where: { id: appId }, + data: { isVisible, score } + }); + }, + async updateApp({ appId, name, slogan, summary, description, score, releasedAt, downloadUrl, legalText, legalUrl }) { + await fastify.db.app.update({ + where: { id: appId }, + data: { name, slogan, summary, description, score, releasedAt, downloadUrl, legalText, legalUrl } + }); + }, + async updateAppMediaExcludeGallery({ appId, usage, image, thumbnail }) { + await fastify.db.appMedia.updateMany({ where: { appId, usage, type: AppMedia.type.image }, data: { image, thumbnail } }); + }, + async updateAppMediaGallery({ appId, video, images }) { + const deleteOld = fastify.db.appMedia.deleteMany({ where: { appId, usage: AppMedia.usage.gallery } }); + const createNewVideo = video.map(item => + fastify.db.appMedia.create({ + data: { + appId, image: item.image, thumbnail: item.thumbnail, video: item.video, usage: AppMedia.usage.gallery, type: AppMedia.type.video + } + }) + ); + const createNewImages = images.map(item => + fastify.db.appMedia.create({ + data: { + appId, image: item.image, thumbnail: item.thumbnail, usage: AppMedia.usage.gallery, type: AppMedia.type.image, + } + }) + ); + await fastify.db.$transaction([deleteOld, ...createNewVideo, ...createNewImages]); + }, + async createAppSocialLink({ appId, name, brand, url }) { + const data = await fastify.db.appSocialLink.create({ data: { appId, name, brand, url } }); + return data; + }, + async deleteAppSocialLink({ socialLinkId }) { + await fastify.db.appSocialLink.delete({ where: { id: socialLinkId } }); + }, + async updateAppPublishers({ appId, publishers }) { + if (!publishers || publishers.length === 0) return; + const deleteOld = fastify.db.appPublisherRef.deleteMany({ where: { appId } }); + const createNew = publishers.map(organizationId => fastify.db.appPublisherRef.create({ data: { appId, organizationId } })); + await fastify.db.$transaction([deleteOld, ...createNew]); + }, + async updateAppDevelopers({ appId, developers }) { + if (!developers || developers.length === 0) return; + const deleteOld = fastify.db.appDeveloperRef.deleteMany({ where: { appId } }); + const createNew = developers.map(organizationId => fastify.db.appDeveloperRef.create({ data: { appId, organizationId } })); + await fastify.db.$transaction([deleteOld, ...createNew]); + }, + async updateAppFeatures({ appId, features }) { + if (!features || features.length === 0) return; + const createNewRefs = features.map(featureId => fastify.db.appFeatureRef.create({ data: { appId, tagId: featureId } })); + await fastify.db.$transaction(createNewRefs); + }, + async deleteAppFeature({ appId, featureId }) { + await fastify.db.appFeatureRef.deleteMany({ where: { appId, tagId: featureId } }); + }, + async updateAppGenres({ appId, genres }) { + if (!genres || genres.length === 0) return; + const createNewRefs = genres.map(genreId => fastify.db.appGenreRef.create({ data: { appId, tagId: genreId } })); + await fastify.db.$transaction(createNewRefs); + }, + async deleteAppGenre({ appId, genreId }) { + await fastify.db.appGenreRef.deleteMany({ where: { appId, tagId: genreId } }); + }, + async deleteAppTag({ appId, tagId }) { + await fastify.db.appUserTagRef.deleteMany({ where: { appId, tagId } }); // all users' tag relationships will be deleted + }, + async updateAppPlatforms({ appId, platforms }) { + if (!platforms || platforms.length === 0) return; + const deleteOld = fastify.db.appPlatform.deleteMany({ where: { appId } }); + const createNewPlatforms = platforms.map(platform => + fastify.db.appPlatform.create({ + data: { + appId, + os: platform.os, + requirements: JSON.stringify(platform.requirements), + } + }) + ); + await fastify.db.$transaction([deleteOld, ...createNewPlatforms]); + }, + async createAppAward({ appId, image, url }) { + const data = await fastify.db.appAward.create({ + data: { appId, image, url } + }); + return data; + }, + async deleteAppAward({ appId, awardId }) { + await fastify.db.appAward.delete({ where: { id: awardId, appId, } }); + }, + async updateAppLanguages({ appId, text, audio, caption }) { + await fastify.db.appLanguages.upsert({ where: { appId }, create: { text, audio, caption, appId }, update: { text, audio, caption } }); + } }); next(); }; diff --git a/ktap-server/src/models/review.js b/ktap-server/src/models/review.js index a01c853..238fe9b 100644 --- a/ktap-server/src/models/review.js +++ b/ktap-server/src/models/review.js @@ -262,6 +262,15 @@ const review = async (fastify, opts, next) => { (SELECT count(*) FROM ReviewThumb WHERE direction = 'down' AND review_id = ${id}) AS downs `)[0]; }, + async createProReview({ appId, name, url, summary, score }) { + const data = await fastify.db.proReview.create({ + data: { appId, name, url, summary, score } + }); + return data; + }, + async deleteProReview({ proReviewId }) { + await fastify.db.proReview.delete({ where: { id: proReviewId } }); + }, }); next(); }; diff --git a/ktap-server/src/routes/admin/apps.js b/ktap-server/src/routes/admin/apps.js index df30b41..49e495b 100644 --- a/ktap-server/src/routes/admin/apps.js +++ b/ktap-server/src/routes/admin/apps.js @@ -34,58 +34,47 @@ const apps = async function (fastify, opts) { // 头图(Head)、宣传横图(Landscape)、宣传竖图(Portrait)、画廊(Gallery)、品牌商标(Logo) // 其他的可以直接进入详情页面自行更改 fastify.post('', async (req, reply) => { - const newApp = await fastify.db.app.create({ - data: { - name: req.body.name, - isVisible: false, // 初始为不可见,操作完成之后再可见 - media: { - create: [ - { type: AppMedia.type.image, usage: AppMedia.usage.head, image: 'https://placehold.co/460x215/png', thumbnail: 'https://placehold.co/292x136/png' }, - { type: AppMedia.type.image, usage: AppMedia.usage.landscape, image: 'https://placehold.co/2560x1440/png', thumbnail: 'https://placehold.co/616x353/png' }, - { type: AppMedia.type.image, usage: AppMedia.usage.portrait, image: 'https://placehold.co/1200x1600/png', thumbnail: 'https://placehold.co/374x448/png' }, - { type: AppMedia.type.image, usage: AppMedia.usage.logo, image: 'https://placehold.co/400x400/png', thumbnail: 'https://placehold.co/128x128/png' } - ] - } - }, - }); - return reply.code(201).send({ data: { id: newApp.id } }); + try { + const newApp = await fastify.app.createApp({ name: req.body.name }); + return reply.code(201).send({ data: { id: newApp.id } }); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.put('/:id/basis', async (req, reply) => { const id = Number(req.params.id) || 0; - await fastify.db.app.update({ - where: { id }, - data: { - isVisible: req.body.isVisible, - score: Number(req.body.score) - } - }); - return reply.code(204).send(); + const score = Number(req.body.score); + const isVisible = req.body.isVisible; + try { + await fastify.app.updateAppBasis({ appId: id, isVisible, score }); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.put('/:id', async (req, reply) => { const id = Number(req.params.id) || 0; const { app } = req.body; - if (app) { - await fastify.db.app.update({ - where: { id }, - data: { - name: app.name, - slogan: app.slogan, - summary: app.summary, - description: app.description, - score: app.score, - releasedAt: app.releasedAt, - downloadUrl: app.downloadUrl, - legalText: app.legalText, + try { + if (app) { + await fastify.app.updateApp({ + appId: id, name: app.name, slogan: app.slogan, + summary: app.summary, description: app.description, + score: app.score, releasedAt: app.releasedAt, + downloadUrl: app.downloadUrl, legalText: app.legalText, legalUrl: app.legalUrl, - } - }); + }); + } + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); - fastify.get('/:id', async (req, reply) => { const id = Number(req.params.id) || 0; const data = await fastify.db.app.findUnique({ where: { id }, }); @@ -101,38 +90,19 @@ const apps = async function (fastify, opts) { fastify.put('/:id/media/:usage', async (req, reply) => { const id = Number(req.params.id) || 0; const usage = req.params.usage; - if (usage !== AppMedia.usage.gallery) { - const { image, thumbnail } = req.body; - await fastify.db.appMedia.updateMany({ where: { appId: id, usage, type: AppMedia.type.image }, data: { image, thumbnail } }); - } else { - const { video, images } = req.body; - const deleteOld = fastify.db.appMedia.deleteMany({ where: { appId: id, usage: AppMedia.usage.gallery } }); - const createNewVideo = video.map(item => - fastify.db.appMedia.create({ - data: { - appId: id, - image: item.image, - thumbnail: item.thumbnail, - video: item.video, - usage: AppMedia.usage.gallery, - type: AppMedia.type.video - } - }) - ); - const createNewImages = images.map(item => - fastify.db.appMedia.create({ - data: { - appId: id, - image: item.image, - thumbnail: item.thumbnail, - usage: AppMedia.usage.gallery, - type: AppMedia.type.image, - } - }) - ); - await fastify.db.$transaction([deleteOld, ...createNewVideo, ...createNewImages]); + try { + if (usage !== AppMedia.usage.gallery) { + const { image, thumbnail } = req.body; + await fastify.app.updateAppMediaExcludeGallery({ appId: id, usage, image, thumbnail }); + } else { + const { video, images } = req.body; + await fastify.app.updateAppMediaGallery({ appId: id, usage, video, images }); + } + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); fastify.get('/:id/social-links', async (req, reply) => { @@ -149,18 +119,24 @@ const apps = async function (fastify, opts) { fastify.post('/:id/social-links', async (req, reply) => { const id = Number(req.params.id) || 0; const { name, url, brand } = req.body; - const data = await fastify.db.appSocialLink.create({ - data: { appId: id, name, brand, url, } - }); - return reply.code(201).send({ data }); + try { + const data = await fastify.app.createAppSocialLink({ appId: id, name, url, brand }); + return reply.code(201).send({ data }); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.delete('/:id/social-links/:socialLinkId', async (req, reply) => { const socialLinkId = Number(req.params.socialLinkId) || 0; - await fastify.db.appSocialLink.delete({ - where: { id: socialLinkId }, - }); - return reply.code(204).send(); + try { + await fastify.app.deleteAppSocialLink({ socialLinkId }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.get('/:id/publishers', async (req, reply) => { @@ -180,15 +156,15 @@ const apps = async function (fastify, opts) { fastify.put('/:id/publishers', async (req, reply) => { const id = Number(req.params.id) || 0; const { publishers } = req.body; - if (publishers) { - const deleteOld = fastify.db.appPublisherRef.deleteMany({ where: { appId: id } }); - const createNew = publishers.map(organizationId => fastify.db.appPublisherRef.create({ data: { appId: id, organizationId } })); - await fastify.db.$transaction([deleteOld, ...createNew]); + try { + await fastify.app.updateAppPublishers({ appId: id, publishers }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); - fastify.get('/:id/developers', async (req, reply) => { const id = Number(req.params.id) || 0; let data = await fastify.db.appDeveloperRef.findMany({ @@ -206,12 +182,13 @@ const apps = async function (fastify, opts) { fastify.put('/:id/developers', async (req, reply) => { const id = Number(req.params.id) || 0; const { developers } = req.body; - if (developers) { - const deleteOld = fastify.db.appDeveloperRef.deleteMany({ where: { appId: id } }); - const createNew = developers.map(organizationId => fastify.db.appDeveloperRef.create({ data: { appId: id, organizationId } })); - await fastify.db.$transaction([deleteOld, ...createNew]); + try { + await fastify.app.updateAppDevelopers({ appId: id, developers }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); fastify.get('/:id/features', async (req, reply) => { @@ -231,18 +208,25 @@ const apps = async function (fastify, opts) { fastify.put('/:id/features', async (req, reply) => { const id = Number(req.params.id) || 0; const { features } = req.body; - if (features) { - const createNewRefs = features.map(featureId => fastify.db.appFeatureRef.create({ data: { appId: id, tagId: featureId } })); - await fastify.db.$transaction(createNewRefs); + try { + await fastify.app.updateAppFeatures({ appId: id, features }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); fastify.delete('/:id/features/:featureId', async (req, reply) => { const id = Number(req.params.id) || 0; const featureId = Number(req.params.featureId) || 0; - await fastify.db.appFeatureRef.deleteMany({ where: { appId: id, tagId: featureId } }); - return reply.code(204).send(); + try { + await fastify.app.deleteAppFeature({ appId: id, featureId }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.get('/:id/genres', async (req, reply) => { @@ -262,18 +246,25 @@ const apps = async function (fastify, opts) { fastify.put('/:id/genres', async (req, reply) => { const id = Number(req.params.id) || 0; const { genres } = req.body; - if (genres) { - const createNewRefs = genres.map(genreId => fastify.db.appGenreRef.create({ data: { appId: id, tagId: genreId } })); - await fastify.db.$transaction(createNewRefs); + try { + await fastify.app.updateAppGenres({ appId: id, genres }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); fastify.delete('/:id/genres/:genreId', async (req, reply) => { const id = Number(req.params.id) || 0; const genreId = Number(req.params.genreId) || 0; - await fastify.db.appGenreRef.deleteMany({ where: { appId: id, tagId: genreId } }); - return reply.code(204).send(); + try { + await fastify.app.deleteAppGenre({ appId: id, genreId }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.get('/:id/tags', async (req, reply) => { @@ -307,8 +298,13 @@ const apps = async function (fastify, opts) { fastify.delete('/:id/tags/:tagId', async (req, reply) => { const id = Number(req.params.id) || 0; const tagId = Number(req.params.tagId) || 0; - await fastify.db.appUserTagRef.deleteMany({ where: { appId: id, tagId } }); // all users' relationships will be deleted - return reply.code(204).send(); + try { + await fastify.app.deleteAppTag({ appId: id, tagId }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.get('/:id/platforms', async (req, reply) => { @@ -321,20 +317,13 @@ const apps = async function (fastify, opts) { fastify.put('/:id/platforms', async (req, reply) => { const id = Number(req.params.id) || 0; const { platforms } = req.body; - if (platforms) { - const deleteOld = fastify.db.appPlatform.deleteMany({ where: { appId: id } }); - const createNewPlatforms = platforms.map(platform => - fastify.db.appPlatform.create({ - data: { - appId: id, - os: platform.os, - requirements: JSON.stringify(platform.requirements), - } - }) - ); - await fastify.db.$transaction([deleteOld, ...createNewPlatforms]); + try { + await fastify.app.updateAppPlatforms({ appId: id, platforms }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); } - return reply.code(204).send(); }); fastify.get('/:id/pro-reviews', async (req, reply) => { @@ -345,22 +334,25 @@ const apps = async function (fastify, opts) { fastify.post('/:id/pro-reviews', async (req, reply) => { const id = Number(req.params.id) || 0; - const data = await fastify.db.proReview.create({ - data: { - name: req.body.name, - url: req.body.url, - summary: req.body.url, - score: req.body.score, - appId: id, - } - }); - return reply.code(201).send({ data }); + const { name, url, summary, score } = req.body; + try { + const data = await fastify.review.createProReview({ appId: id, name, url, summary, score }); + return reply.code(201).send({ data }); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.delete('/:id/pro-reviews/:proReviewId', async (req, reply) => { const proReviewId = Number(req.params.proReviewId) || 0; - await fastify.db.proReview.delete({ where: { id: proReviewId } }); - return reply.code(204).send(); + try { + await fastify.review.deleteProReview({ proReviewId }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.get('/:id/awards', async (req, reply) => { @@ -371,20 +363,26 @@ const apps = async function (fastify, opts) { fastify.post('/:id/awards', async (req, reply) => { const id = Number(req.params.id) || 0; - const data = await fastify.db.appAward.create({ - data: { - image: req.body.image, - url: req.body.url, - appId: id, - } - }); - return reply.code(201).send({ data }); + const { image, url } = req.body; + try { + const data = await fastify.app.createAppAward({ appId: id, image, url }); + return reply.code(201).send({ data }); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.delete('/:id/awards/:awardId', async (req, reply) => { + const id = Number(req.params.id) || 0; const awardId = Number(req.params.awardId) || 0; - await fastify.db.appAward.delete({ where: { id: awardId } }); - return reply.code(204).send(); + try { + await fastify.app.deleteAppAward({ appId: id, awardId }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.get('/:id/languages', async (req, reply) => { @@ -396,8 +394,13 @@ const apps = async function (fastify, opts) { fastify.put('/:id/languages', async (req, reply) => { const id = Number(req.params.id) || 0; const { text, audio, caption } = req.body; - await fastify.db.appLanguages.upsert({ where: { appId: id }, create: { text, audio, caption, appId: id }, update: { text, audio, caption } }); - return reply.code(204).send(); + try { + await fastify.app.updateAppLanguages({ appId: id, text, audio, caption }); + return reply.code(204).send(); + } catch (err) { + fastify.log.warn(err); + return reply.code(400).send({ message: err.message }); + } }); fastify.post('/monkey/steam', async (req, reply) => { diff --git a/ktap-server/yarn.lock b/ktap-server/yarn.lock index a0ff3ce..d0787ff 100644 --- a/ktap-server/yarn.lock +++ b/ktap-server/yarn.lock @@ -44,7 +44,7 @@ resolved "https://registry.npmmirror.com/@fastify/deepmerge/-/deepmerge-1.3.0.tgz#8116858108f0c7d9fd460d05a7d637a13fe3239a" integrity sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A== -"@fastify/error@^3.0.0", "@fastify/error@^3.2.0": +"@fastify/error@^3.0.0", "@fastify/error@^3.4.0": version "3.4.0" resolved "https://registry.npmmirror.com/@fastify/error/-/error-3.4.0.tgz#30df6601f4edce57a05ec5caaa90a28025a8554a" integrity sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ== @@ -100,10 +100,10 @@ http-errors "2.0.0" mime "^3.0.0" -"@fastify/sensible@^5.3.0": - version "5.3.0" - resolved "https://registry.npmmirror.com/@fastify/sensible/-/sensible-5.3.0.tgz#bd82cc20059f84de893c2a2f4b9438812d63705c" - integrity sha512-/7n8kMO6G7up05MOohZ7OX3u2pUGdfOU8ai2XzY3TNSguVYUcQfyu+A0On41ijxSKo9Z9US5OMfEy+KITF4RQQ== +"@fastify/sensible@^5.4.0": + version "5.4.0" + resolved "https://registry.npmmirror.com/@fastify/sensible/-/sensible-5.4.0.tgz#535c1e6c3b3209db0d51ccdcc80a585d690b48fc" + integrity sha512-pncmQyXmHPmhU4FRAjUr4WYAt1v22ZPed3gujyTE8hPNaiTCOjAox7nGYYDY1G9NWCOAfb16CX8dSTajFtnqAw== dependencies: "@lukeed/ms" "^2.0.1" fast-deep-equal "^3.1.1" @@ -126,9 +126,9 @@ p-limit "^3.1.0" "@fastify/swagger-ui@^1.8.0": - version "1.10.0" - resolved "https://registry.npmmirror.com/@fastify/swagger-ui/-/swagger-ui-1.10.0.tgz#20b731bbb1af20581f321703d2eddc08dabe4310" - integrity sha512-vps8KRDQ8JWQGG9Dh6fCOzAc81Y5a6RnCU/Sumyw3n7+HXJSYExnLLgv4aS9eRFSODtQ8kzZJ//IxAbf0nZ+sQ== + version "1.10.1" + resolved "https://registry.npmmirror.com/@fastify/swagger-ui/-/swagger-ui-1.10.1.tgz#513c19e28b3393f037a6c88f5ad7cba2935c57a8" + integrity sha512-u3EJqNKvVr3X+6jY5i6pbs6/tXCrSlqc2Y+PVjnHBTOGh/d36uHMz+z4jPFy9gie2my6iHUrAdM8itlVmoUjog== dependencies: "@fastify/static" "^6.0.0" fastify-plugin "^4.0.0" @@ -137,9 +137,9 @@ yaml "^2.2.2" "@fastify/swagger@^8.3.1": - version "8.11.0" - resolved "https://registry.npmmirror.com/@fastify/swagger/-/swagger-8.11.0.tgz#94397b2d87a955ded1e60bd1fad25195b961a13c" - integrity sha512-K8hAWr3wTBUojjJfNUy0TAl766ga2O/L5mWVbBZ6MImOOB3GYUgqNXC4MKrOejt8Y4Ps4cdKjkgzWLcUtg8SFg== + version "8.12.0" + resolved "https://registry.npmmirror.com/@fastify/swagger/-/swagger-8.12.0.tgz#699fb5b1fb862d85a3b9cfb86a0f504703bfe69b" + integrity sha512-IMRc0xYuzRvtFDMuaWHyVbvM7CuAi0g3o2jaVgLDvETXPrXWAMWsHYR5niIdWBDPgGUq+soHkag1DKXyhPDB0w== dependencies: fastify-plugin "^4.0.0" json-schema-resolver "^2.0.0" @@ -636,7 +636,7 @@ events@^3.3.0: resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -fast-content-type-parse@^1.0.0: +fast-content-type-parse@^1.1.0: version "1.1.0" resolved "https://registry.npmmirror.com/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz#4087162bf5af3294d4726ff29b334f72e3a1092c" integrity sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ== @@ -656,7 +656,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-json-stringify@^5.7.0: +fast-json-stringify@^5.7.0, fast-json-stringify@^5.8.0: version "5.8.0" resolved "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-5.8.0.tgz#b229ed01ac5f92f3b82001a916c31324652f46d7" integrity sha512-VVwK8CFMSALIvt14U8AvrSzQAwN/0vaVRiFFUVlpnXSnDGrSkOAO5MtzyN8oQNjLd5AqTW5OZRgyjoNuAuR3jQ== @@ -712,27 +712,27 @@ fastify-plugin@^4.0.0, fastify-plugin@^4.2.1: resolved "https://registry.npmmirror.com/fastify-plugin/-/fastify-plugin-4.5.1.tgz#44dc6a3cc2cce0988bc09e13f160120bbd91dbee" integrity sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ== -fastify@^4.24.0: - version "4.24.0" - resolved "https://registry.npmmirror.com/fastify/-/fastify-4.24.0.tgz#4edb911ae3ff6844764bdf7ac1afe8366d30d80f" - integrity sha512-6Uu2cCAV1UgexPnWKchgRt77lng9ivNmyFhPMcgUbJ4VaVBE1l6aYluiYZiVsgOBFpHrmdj7FD6n1aHswln4yQ== +fastify@^4.24.2: + version "4.24.2" + resolved "https://registry.npmmirror.com/fastify/-/fastify-4.24.2.tgz#fad5d42b1abd112aac6ee699e40fec13a2bcbfa8" + integrity sha512-V/7fdhFas7HoAyjD8ha8wPCeiRLUzPgwwM5dSSUx/eBUv7GvG61YzjggqOchMOsa7Sw32MNN4uCCoFrl+9ccJA== dependencies: "@fastify/ajv-compiler" "^3.5.0" - "@fastify/error" "^3.2.0" + "@fastify/error" "^3.4.0" "@fastify/fast-json-stringify-compiler" "^4.3.0" abstract-logging "^2.0.1" avvio "^8.2.1" - fast-content-type-parse "^1.0.0" - fast-json-stringify "^5.7.0" + fast-content-type-parse "^1.1.0" + fast-json-stringify "^5.8.0" find-my-way "^7.7.0" - light-my-request "^5.9.1" - pino "^8.12.0" + light-my-request "^5.11.0" + pino "^8.16.0" process-warning "^2.2.0" proxy-addr "^2.0.7" rfdc "^1.3.0" - secure-json-parse "^2.5.0" - semver "^7.5.0" - toad-cache "^3.2.0" + secure-json-parse "^2.7.0" + semver "^7.5.4" + toad-cache "^3.3.0" fastparallel@^2.2.0: version "2.4.1" @@ -963,7 +963,7 @@ json-schema-traverse@^1.0.0: resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -light-my-request@^5.9.1: +light-my-request@^5.11.0: version "5.11.0" resolved "https://registry.npmmirror.com/light-my-request/-/light-my-request-5.11.0.tgz#90e446c303b3a47b59df38406d5f5c2cf224f2d1" integrity sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA== @@ -1242,7 +1242,7 @@ pino-std-serializers@^6.0.0: resolved "https://registry.npmmirror.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== -pino@^8.12.0: +pino@^8.16.0: version "8.16.0" resolved "https://registry.npmmirror.com/pino/-/pino-8.16.0.tgz#2465012a1d11fa2e7a0545032f636e203990ae26" integrity sha512-UUmvQ/7KTZt/vHjhRrnyS7h+J7qPBQnpG80V56xmIC+o9IqYmQOw/UIny9S9zYDfRBR0ClouCr464EkBMIT7Fw== @@ -1404,7 +1404,7 @@ sanitize-html@^2.11.0: parse-srcset "^1.0.2" postcss "^8.3.11" -secure-json-parse@^2.4.0, secure-json-parse@^2.5.0: +secure-json-parse@^2.4.0, secure-json-parse@^2.7.0: version "2.7.0" resolved "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== @@ -1414,7 +1414,7 @@ semver@^6.0.0: resolved "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5, semver@^7.5.0: +semver@^7.3.5, semver@^7.5.4: version "7.5.4" resolved "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -1545,7 +1545,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toad-cache@^3.2.0: +toad-cache@^3.3.0: version "3.3.0" resolved "https://registry.npmmirror.com/toad-cache/-/toad-cache-3.3.0.tgz#5b7dc67b36bc8b960567eb77bdf9ac6c26f204a1" integrity sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg== @@ -1631,9 +1631,9 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^2.2.2: - version "2.3.2" - resolved "https://registry.npmmirror.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144" - integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg== + version "2.3.3" + resolved "https://registry.npmmirror.com/yaml/-/yaml-2.3.3.tgz#01f6d18ef036446340007db8e016810e5d64aad9" + integrity sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ== yocto-queue@^0.1.0: version "0.1.0" diff --git a/ktap-ui-web/src/assets/css/rdp.css b/ktap-ui-web/src/assets/css/rdp.css index 5562c0e..594a50b 100644 --- a/ktap-ui-web/src/assets/css/rdp.css +++ b/ktap-ui-web/src/assets/css/rdp.css @@ -13,8 +13,7 @@ .rdp-day_selected, .rdp-day_selected:focus-visible, .rdp-day_selected:hover { - color : inherit; + color : black; opacity : 1; - border: 2px solid white; - background-color: transparent; + background-color: white; } \ No newline at end of file diff --git a/ktap-ui-web/src/components/rdpicker.jsx b/ktap-ui-web/src/components/rdpicker.jsx index 74eddc2..8c8937f 100644 --- a/ktap-ui-web/src/components/rdpicker.jsx +++ b/ktap-ui-web/src/components/rdpicker.jsx @@ -20,7 +20,7 @@ function RdPicker({ mode, value, onSelect = () => { } }) { const [css, theme] = useStyletron(); return ( + } placement='top'>
setApp(prev => { return { ...prev, description: e.target.value } })} /> 发布于}> - setApp(prev => { return { ...prev, releasedAt: date } })} /> + setApp(prev => { return { ...prev, releasedAt: date } })} /> 下载链接}> setApp(prev => { return { ...prev, downloadUrl: e.target.value } })} /> diff --git a/ktap-ui-web/src/pages/admin-panel/apps/app-detail-languages.jsx b/ktap-ui-web/src/pages/admin-panel/apps/app-detail-languages.jsx index cf2054b..edf327a 100644 --- a/ktap-ui-web/src/pages/admin-panel/apps/app-detail-languages.jsx +++ b/ktap-ui-web/src/pages/admin-panel/apps/app-detail-languages.jsx @@ -21,9 +21,9 @@ function AppDetailLanguages({ data }) { const res = await fetch(`/admin/apps/${data.id}/languages`); if (res.ok) { const json = await res.json(); - setText(json.data.text); - setAudio(json.data.audio); - setCaption(json.data.caption); + setText(json.data?.text || ''); + setAudio(json.data?.audio || ''); + setCaption(json.data?.caption || ''); } } finally { setIsLoading(false); diff --git a/ktap-ui-web/src/pages/admin-panel/apps/app-detail-tags.jsx b/ktap-ui-web/src/pages/admin-panel/apps/app-detail-tags.jsx index 58eb014..df7d990 100644 --- a/ktap-ui-web/src/pages/admin-panel/apps/app-detail-tags.jsx +++ b/ktap-ui-web/src/pages/admin-panel/apps/app-detail-tags.jsx @@ -220,9 +220,9 @@ function FeaturesBlock({ data }) { - + {features && features.map((feature, index) => ( - { + { e.preventDefault(); handleDelete(feature.id); }}>{feature.name} @@ -284,7 +284,7 @@ function TagsBlock({ data }) { {tags && tags.map((tag, index) => ( - { + { e.preventDefault(); handleDelete(tag.id); }}>{tag.name} diff --git a/ktap-ui-web/src/pages/apps/tab-details-languages.jsx b/ktap-ui-web/src/pages/apps/tab-details-languages.jsx index 30e0996..2fb26e3 100644 --- a/ktap-ui-web/src/pages/apps/tab-details-languages.jsx +++ b/ktap-ui-web/src/pages/apps/tab-details-languages.jsx @@ -1,9 +1,9 @@ -import React from 'react'; -import { LabelLarge, LabelSmall } from 'baseui/typography'; import { Block } from 'baseui/block'; +import { LabelLarge, LabelSmall } from 'baseui/typography'; + +export default function TabDetailsLanguages({ app }) { + const { text, audio } = app?.languages || { text: '', audio: '' }; -function TabDetailsLanguages({ app }) { - const { text, audio } = app.languages; return ( @@ -29,5 +29,4 @@ function TabDetailsLanguages({ app }) { ); -} -export default TabDetailsLanguages; \ No newline at end of file +} \ No newline at end of file diff --git a/ktap-ui-web/src/pages/settings/profile.jsx b/ktap-ui-web/src/pages/settings/profile.jsx index 5245959..c60b12e 100644 --- a/ktap-ui-web/src/pages/settings/profile.jsx +++ b/ktap-ui-web/src/pages/settings/profile.jsx @@ -106,9 +106,7 @@ function SettingsForm({ setNotification }) { /> 生日}> - { - if (date) setForm({ ...form, birthday: DateTime.formatShort(date) }) - }} /> + setForm({ ...form, birthday: DateTime.formatShort(date) })} /> 简介} counter={{ length: form.bio.length, maxLength: 255 }}>