mirror of
https://github.com/wgh136/nysoure.git
synced 2025-12-16 07:51:14 +00:00
Compare commits
5 Commits
ddd856529b
...
5a5c2edfda
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a5c2edfda | |||
| d860bdf06a | |||
| a3de195eca | |||
| 6cabff8f8d | |||
| 78f6130b23 |
@@ -263,6 +263,7 @@ export const i18nData = {
|
|||||||
"Optional": "可选",
|
"Optional": "可选",
|
||||||
"Download": "下载",
|
"Download": "下载",
|
||||||
"Notifications": "通知",
|
"Notifications": "通知",
|
||||||
|
"Release Date": "发售日期",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-TW": {
|
"zh-TW": {
|
||||||
@@ -529,6 +530,7 @@ export const i18nData = {
|
|||||||
"Optional": "可選",
|
"Optional": "可選",
|
||||||
"Download": "下載",
|
"Download": "下載",
|
||||||
"Notifications": "通知",
|
"Notifications": "通知",
|
||||||
|
"Release Date": "發售日期",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export interface CreateResourceParams {
|
|||||||
title: string;
|
title: string;
|
||||||
alternative_titles: string[];
|
alternative_titles: string[];
|
||||||
links: RLink[];
|
links: RLink[];
|
||||||
|
release_date?: string;
|
||||||
tags: number[];
|
tags: number[];
|
||||||
article: string;
|
article: string;
|
||||||
images: number[];
|
images: number[];
|
||||||
@@ -77,6 +78,7 @@ export interface Resource {
|
|||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
|
release_date?: string;
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
image?: Image;
|
image?: Image;
|
||||||
author: User;
|
author: User;
|
||||||
@@ -89,6 +91,7 @@ export interface ResourceDetails {
|
|||||||
links: RLink[];
|
links: RLink[];
|
||||||
article: string;
|
article: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
releaseDate?: string;
|
||||||
tags: Tag[];
|
tags: Tag[];
|
||||||
images: Image[];
|
images: Image[];
|
||||||
files: RFile[];
|
files: RFile[];
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import CharacterEditer, { FetchVndbCharactersButton } from "../components/charac
|
|||||||
export default function EditResourcePage() {
|
export default function EditResourcePage() {
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
const [altTitles, setAltTitles] = useState<string[]>([]);
|
const [altTitles, setAltTitles] = useState<string[]>([]);
|
||||||
|
const [releaseDate, setReleaseDate] = useState<string | undefined>(undefined);
|
||||||
const [tags, setTags] = useState<Tag[]>([]);
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
const [article, setArticle] = useState<string>("");
|
const [article, setArticle] = useState<string>("");
|
||||||
const [images, setImages] = useState<number[]>([]);
|
const [images, setImages] = useState<number[]>([]);
|
||||||
@@ -61,6 +62,7 @@ export default function EditResourcePage() {
|
|||||||
setLinks(data.links ?? []);
|
setLinks(data.links ?? []);
|
||||||
setGalleryImages(data.gallery ?? []);
|
setGalleryImages(data.gallery ?? []);
|
||||||
setGalleryNsfw(data.galleryNsfw ?? []);
|
setGalleryNsfw(data.galleryNsfw ?? []);
|
||||||
|
setReleaseDate(data.releaseDate ?? undefined);
|
||||||
setCharacters(data.characters ?? []);
|
setCharacters(data.characters ?? []);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -108,6 +110,7 @@ export default function EditResourcePage() {
|
|||||||
gallery: galleryImages,
|
gallery: galleryImages,
|
||||||
gallery_nsfw: galleryNsfw,
|
gallery_nsfw: galleryNsfw,
|
||||||
characters: characters,
|
characters: characters,
|
||||||
|
release_date: releaseDate,
|
||||||
});
|
});
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -194,6 +197,14 @@ export default function EditResourcePage() {
|
|||||||
{t("Add Alternative Title")}
|
{t("Add Alternative Title")}
|
||||||
</button>
|
</button>
|
||||||
<div className={"h-2"}></div>
|
<div className={"h-2"}></div>
|
||||||
|
<p className={"my-1"}>{t("Release Date")}</p>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
className="input"
|
||||||
|
value={releaseDate || ""}
|
||||||
|
onChange={(e) => setReleaseDate(e.target.value || undefined)}
|
||||||
|
/>
|
||||||
|
<div className={"h-4"}></div>
|
||||||
<p className={"my-1"}>{t("Links")}</p>
|
<p className={"my-1"}>{t("Links")}</p>
|
||||||
<div className={"flex flex-col"}>
|
<div className={"flex flex-col"}>
|
||||||
{links.map((link, index) => {
|
{links.map((link, index) => {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import CharacterEditer, { FetchVndbCharactersButton } from "../components/charac
|
|||||||
export default function PublishPage() {
|
export default function PublishPage() {
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
const [altTitles, setAltTitles] = useState<string[]>([]);
|
const [altTitles, setAltTitles] = useState<string[]>([]);
|
||||||
|
const [releaseDate, setReleaseDate] = useState<string | undefined>(undefined);
|
||||||
const [tags, setTags] = useState<Tag[]>([]);
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
const [article, setArticle] = useState<string>("");
|
const [article, setArticle] = useState<string>("");
|
||||||
const [images, setImages] = useState<number[]>([]);
|
const [images, setImages] = useState<number[]>([]);
|
||||||
@@ -43,9 +44,14 @@ export default function PublishPage() {
|
|||||||
const data = JSON.parse(oldData);
|
const data = JSON.parse(oldData);
|
||||||
setTitle(data.title || "");
|
setTitle(data.title || "");
|
||||||
setAltTitles(data.alternative_titles || []);
|
setAltTitles(data.alternative_titles || []);
|
||||||
|
setReleaseDate(data.release_date || undefined);
|
||||||
setTags(data.tags || []);
|
setTags(data.tags || []);
|
||||||
setArticle(data.article || "");
|
setArticle(data.article || "");
|
||||||
setImages(data.images || []);
|
setImages(data.images || []);
|
||||||
|
setLinks(data.links || []);
|
||||||
|
setGalleryImages(data.gallery || []);
|
||||||
|
setGalleryNsfw(data.gallery_nsfw || []);
|
||||||
|
setCharacters(data.characters || []);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to parse publish_data from localStorage", e);
|
console.error("Failed to parse publish_data from localStorage", e);
|
||||||
}
|
}
|
||||||
@@ -58,11 +64,16 @@ export default function PublishPage() {
|
|||||||
tags: tags,
|
tags: tags,
|
||||||
article: article,
|
article: article,
|
||||||
images: images,
|
images: images,
|
||||||
|
links: links,
|
||||||
|
gallery: galleryImages,
|
||||||
|
gallery_nsfw: galleryNsfw,
|
||||||
|
characters: characters,
|
||||||
|
release_date: releaseDate,
|
||||||
};
|
};
|
||||||
const dataString = JSON.stringify(data);
|
const dataString = JSON.stringify(data);
|
||||||
localStorage.setItem("publish_data", dataString);
|
localStorage.setItem("publish_data", dataString);
|
||||||
}
|
}
|
||||||
}, [altTitles, article, images, tags, title]);
|
}, [altTitles, article, images, tags, title, links, galleryImages, galleryNsfw, characters, releaseDate]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -105,6 +116,7 @@ export default function PublishPage() {
|
|||||||
const res = await network.createResource({
|
const res = await network.createResource({
|
||||||
title: title,
|
title: title,
|
||||||
alternative_titles: altTitles,
|
alternative_titles: altTitles,
|
||||||
|
release_date: releaseDate,
|
||||||
tags: tags.map((tag) => tag.id),
|
tags: tags.map((tag) => tag.id),
|
||||||
article: article,
|
article: article,
|
||||||
images: images,
|
images: images,
|
||||||
@@ -201,6 +213,14 @@ export default function PublishPage() {
|
|||||||
{t("Add Alternative Title")}
|
{t("Add Alternative Title")}
|
||||||
</button>
|
</button>
|
||||||
<div className={"h-2"}></div>
|
<div className={"h-2"}></div>
|
||||||
|
<p className={"my-1"}>{t("Release Date")}</p>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
className="input"
|
||||||
|
value={releaseDate || ""}
|
||||||
|
onChange={(e) => setReleaseDate(e.target.value || undefined)}
|
||||||
|
/>
|
||||||
|
<div className={"h-4"}></div>
|
||||||
<p className={"my-1"}>{t("Links")}</p>
|
<p className={"my-1"}>{t("Links")}</p>
|
||||||
<div className={"flex flex-col"}>
|
<div className={"flex flex-col"}>
|
||||||
{links.map((link, index) => {
|
{links.map((link, index) => {
|
||||||
|
|||||||
@@ -209,6 +209,14 @@ export default function ResourcePage() {
|
|||||||
</h2>
|
</h2>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{
|
||||||
|
resource.releaseDate ? (
|
||||||
|
<div className={"px-4 py-1 text-sm text-gray-600 dark:text-gray-400 flex items-center"}>
|
||||||
|
<MdOutlineAccessTime size={18} className={"inline-block mr-1"} />
|
||||||
|
{t("Release Date")}: {resource.releaseDate.split("T")[0]}
|
||||||
|
</div>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(
|
navigate(
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"nysoure/server/dao"
|
||||||
"nysoure/server/middleware"
|
"nysoure/server/middleware"
|
||||||
|
"time"
|
||||||
|
|
||||||
"nysoure/server/search"
|
"nysoure/server/search"
|
||||||
|
|
||||||
@@ -20,10 +23,40 @@ func rebuildSearchIndex(c fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateResourceReleaseDate(c fiber.Ctx) error {
|
||||||
|
type Request struct {
|
||||||
|
ResourceID uint `json:"resource_id"`
|
||||||
|
ReleaseDate string `json:"release_date"`
|
||||||
|
}
|
||||||
|
var req Request
|
||||||
|
if err := c.Bind().JSON(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"error": "Invalid request body: " + err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
date, err := time.Parse("2006-01-02", req.ReleaseDate)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||||
|
"error": "Invalid date format: " + err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
err = dao.UpdateResourceReleaseDate(req.ResourceID, date)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to update release date", "error", err)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
"error": "Failed to update release date",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"message": "Release date updated successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func AddDevAPI(router fiber.Router) {
|
func AddDevAPI(router fiber.Router) {
|
||||||
devGroup := router.Group("/dev")
|
devGroup := router.Group("/dev")
|
||||||
devGroup.Use(middleware.DevMiddleware())
|
devGroup.Use(middleware.DevMiddleware())
|
||||||
{
|
{
|
||||||
devGroup.Post("/rebuild_search_index", rebuildSearchIndex)
|
devGroup.Post("/rebuild_search_index", rebuildSearchIndex)
|
||||||
|
devGroup.Post("/update_resource_release_date", updateResourceReleaseDate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -704,3 +704,14 @@ func GetResourceOwnerID(resourceID uint) (uint, error) {
|
|||||||
}
|
}
|
||||||
return uid, nil
|
return uid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateResourceReleaseDate(resourceID uint, releaseDate time.Time) error {
|
||||||
|
result := db.Model(&model.Resource{}).Where("id = ?", resourceID).Update("release_date", releaseDate)
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
return model.NewNotFoundError("Resource not found")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type Resource struct {
|
|||||||
Title string
|
Title string
|
||||||
AlternativeTitles []string `gorm:"serializer:json"`
|
AlternativeTitles []string `gorm:"serializer:json"`
|
||||||
Links []Link `gorm:"serializer:json"`
|
Links []Link `gorm:"serializer:json"`
|
||||||
|
ReleaseDate *time.Time
|
||||||
Article string
|
Article string
|
||||||
Images []Image `gorm:"many2many:resource_images;"`
|
Images []Image `gorm:"many2many:resource_images;"`
|
||||||
Tags []Tag `gorm:"many2many:resource_tags;"`
|
Tags []Tag `gorm:"many2many:resource_tags;"`
|
||||||
@@ -32,12 +33,13 @@ type Link struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResourceView struct {
|
type ResourceView struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
Tags []TagView `json:"tags"`
|
ReleaseDate *time.Time `json:"release_date,omitempty"`
|
||||||
Image *ImageView `json:"image"`
|
Tags []TagView `json:"tags"`
|
||||||
Author UserView `json:"author"`
|
Image *ImageView `json:"image"`
|
||||||
|
Author UserView `json:"author"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceDetailView struct {
|
type ResourceDetailView struct {
|
||||||
@@ -47,6 +49,7 @@ type ResourceDetailView struct {
|
|||||||
Links []Link `json:"links"`
|
Links []Link `json:"links"`
|
||||||
Article string `json:"article"`
|
Article string `json:"article"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
ReleaseDate *time.Time `json:"releaseDate,omitempty"`
|
||||||
Tags []TagView `json:"tags"`
|
Tags []TagView `json:"tags"`
|
||||||
Images []ImageView `json:"images"`
|
Images []ImageView `json:"images"`
|
||||||
Files []FileView `json:"files"`
|
Files []FileView `json:"files"`
|
||||||
@@ -81,12 +84,13 @@ func (r *Resource) ToView() ResourceView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ResourceView{
|
return ResourceView{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
Title: r.Title,
|
Title: r.Title,
|
||||||
CreatedAt: r.CreatedAt,
|
CreatedAt: r.CreatedAt,
|
||||||
Tags: tags,
|
ReleaseDate: r.ReleaseDate,
|
||||||
Image: image,
|
Tags: tags,
|
||||||
Author: r.User.ToView(),
|
Image: image,
|
||||||
|
Author: r.User.ToView(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +119,7 @@ func (r *Resource) ToDetailView() ResourceDetailView {
|
|||||||
Links: r.Links,
|
Links: r.Links,
|
||||||
Article: r.Article,
|
Article: r.Article,
|
||||||
CreatedAt: r.CreatedAt,
|
CreatedAt: r.CreatedAt,
|
||||||
|
ReleaseDate: r.ReleaseDate,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
Images: images,
|
Images: images,
|
||||||
Files: files,
|
Files: files,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type ResourceParams struct {
|
|||||||
Title string `json:"title" binding:"required"`
|
Title string `json:"title" binding:"required"`
|
||||||
AlternativeTitles []string `json:"alternative_titles"`
|
AlternativeTitles []string `json:"alternative_titles"`
|
||||||
Links []model.Link `json:"links"`
|
Links []model.Link `json:"links"`
|
||||||
|
ReleaseDate string `json:"release_date"`
|
||||||
Tags []uint `json:"tags"`
|
Tags []uint `json:"tags"`
|
||||||
Article string `json:"article"`
|
Article string `json:"article"`
|
||||||
Images []uint `json:"images"`
|
Images []uint `json:"images"`
|
||||||
@@ -101,11 +102,20 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
|
|||||||
ImageID: imageID,
|
ImageID: imageID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var date *time.Time
|
||||||
|
if params.ReleaseDate != "" {
|
||||||
|
parsedDate, err := time.Parse("2006-01-02", params.ReleaseDate)
|
||||||
|
if err != nil {
|
||||||
|
return 0, model.NewRequestError("Invalid release date format, expected YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
date = &parsedDate
|
||||||
|
}
|
||||||
r := model.Resource{
|
r := model.Resource{
|
||||||
Title: params.Title,
|
Title: params.Title,
|
||||||
AlternativeTitles: params.AlternativeTitles,
|
AlternativeTitles: params.AlternativeTitles,
|
||||||
Article: params.Article,
|
Article: params.Article,
|
||||||
Links: params.Links,
|
Links: params.Links,
|
||||||
|
ReleaseDate: date,
|
||||||
Images: images,
|
Images: images,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
@@ -529,10 +539,20 @@ func UpdateResource(uid, rid uint, params *ResourceParams) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var date *time.Time
|
||||||
|
if params.ReleaseDate != "" {
|
||||||
|
parsedDate, err := time.Parse("2006-01-02", params.ReleaseDate)
|
||||||
|
if err != nil {
|
||||||
|
return model.NewRequestError("Invalid release date format, expected YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
date = &parsedDate
|
||||||
|
}
|
||||||
|
|
||||||
r.Title = params.Title
|
r.Title = params.Title
|
||||||
r.AlternativeTitles = params.AlternativeTitles
|
r.AlternativeTitles = params.AlternativeTitles
|
||||||
r.Article = params.Article
|
r.Article = params.Article
|
||||||
r.Links = params.Links
|
r.Links = params.Links
|
||||||
|
r.ReleaseDate = date
|
||||||
r.Gallery = gallery
|
r.Gallery = gallery
|
||||||
r.GalleryNsfw = nsfw
|
r.GalleryNsfw = nsfw
|
||||||
r.Characters = characters
|
r.Characters = characters
|
||||||
|
|||||||
Reference in New Issue
Block a user