Compare commits

...

5 Commits

9 changed files with 126 additions and 13 deletions

View File

@@ -263,6 +263,7 @@ export const i18nData = {
"Optional": "可选",
"Download": "下载",
"Notifications": "通知",
"Release Date": "发售日期",
},
},
"zh-TW": {
@@ -529,6 +530,7 @@ export const i18nData = {
"Optional": "可選",
"Download": "下載",
"Notifications": "通知",
"Release Date": "發售日期",
},
},
};

View File

@@ -44,6 +44,7 @@ export interface CreateResourceParams {
title: string;
alternative_titles: string[];
links: RLink[];
release_date?: string;
tags: number[];
article: string;
images: number[];
@@ -77,6 +78,7 @@ export interface Resource {
id: number;
title: string;
created_at: string;
release_date?: string;
tags: Tag[];
image?: Image;
author: User;
@@ -89,6 +91,7 @@ export interface ResourceDetails {
links: RLink[];
article: string;
createdAt: string;
releaseDate?: string;
tags: Tag[];
images: Image[];
files: RFile[];

View File

@@ -25,6 +25,7 @@ import CharacterEditer, { FetchVndbCharactersButton } from "../components/charac
export default function EditResourcePage() {
const [title, setTitle] = useState<string>("");
const [altTitles, setAltTitles] = useState<string[]>([]);
const [releaseDate, setReleaseDate] = useState<string | undefined>(undefined);
const [tags, setTags] = useState<Tag[]>([]);
const [article, setArticle] = useState<string>("");
const [images, setImages] = useState<number[]>([]);
@@ -61,6 +62,7 @@ export default function EditResourcePage() {
setLinks(data.links ?? []);
setGalleryImages(data.gallery ?? []);
setGalleryNsfw(data.galleryNsfw ?? []);
setReleaseDate(data.releaseDate ?? undefined);
setCharacters(data.characters ?? []);
setLoading(false);
} else {
@@ -108,6 +110,7 @@ export default function EditResourcePage() {
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
characters: characters,
release_date: releaseDate,
});
if (res.success) {
setSubmitting(false);
@@ -194,6 +197,14 @@ export default function EditResourcePage() {
{t("Add Alternative Title")}
</button>
<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>
<div className={"flex flex-col"}>
{links.map((link, index) => {

View File

@@ -24,6 +24,7 @@ import CharacterEditer, { FetchVndbCharactersButton } from "../components/charac
export default function PublishPage() {
const [title, setTitle] = useState<string>("");
const [altTitles, setAltTitles] = useState<string[]>([]);
const [releaseDate, setReleaseDate] = useState<string | undefined>(undefined);
const [tags, setTags] = useState<Tag[]>([]);
const [article, setArticle] = useState<string>("");
const [images, setImages] = useState<number[]>([]);
@@ -43,9 +44,14 @@ export default function PublishPage() {
const data = JSON.parse(oldData);
setTitle(data.title || "");
setAltTitles(data.alternative_titles || []);
setReleaseDate(data.release_date || undefined);
setTags(data.tags || []);
setArticle(data.article || "");
setImages(data.images || []);
setLinks(data.links || []);
setGalleryImages(data.gallery || []);
setGalleryNsfw(data.gallery_nsfw || []);
setCharacters(data.characters || []);
} catch (e) {
console.error("Failed to parse publish_data from localStorage", e);
}
@@ -58,11 +64,16 @@ export default function PublishPage() {
tags: tags,
article: article,
images: images,
links: links,
gallery: galleryImages,
gallery_nsfw: galleryNsfw,
characters: characters,
release_date: releaseDate,
};
const dataString = JSON.stringify(data);
localStorage.setItem("publish_data", dataString);
}
}, [altTitles, article, images, tags, title]);
}, [altTitles, article, images, tags, title, links, galleryImages, galleryNsfw, characters, releaseDate]);
const navigate = useNavigate();
const { t } = useTranslation();
@@ -105,6 +116,7 @@ export default function PublishPage() {
const res = await network.createResource({
title: title,
alternative_titles: altTitles,
release_date: releaseDate,
tags: tags.map((tag) => tag.id),
article: article,
images: images,
@@ -201,6 +213,14 @@ export default function PublishPage() {
{t("Add Alternative Title")}
</button>
<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>
<div className={"flex flex-col"}>
{links.map((link, index) => {

View File

@@ -209,6 +209,14 @@ export default function ResourcePage() {
</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
onClick={() => {
navigate(

View File

@@ -1,7 +1,10 @@
package api
import (
"log/slog"
"nysoure/server/dao"
"nysoure/server/middleware"
"time"
"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) {
devGroup := router.Group("/dev")
devGroup.Use(middleware.DevMiddleware())
{
devGroup.Post("/rebuild_search_index", rebuildSearchIndex)
devGroup.Post("/update_resource_release_date", updateResourceReleaseDate)
}
}

View File

@@ -704,3 +704,14 @@ func GetResourceOwnerID(resourceID uint) (uint, error) {
}
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
}

View File

@@ -11,6 +11,7 @@ type Resource struct {
Title string
AlternativeTitles []string `gorm:"serializer:json"`
Links []Link `gorm:"serializer:json"`
ReleaseDate *time.Time
Article string
Images []Image `gorm:"many2many:resource_images;"`
Tags []Tag `gorm:"many2many:resource_tags;"`
@@ -32,12 +33,13 @@ type Link struct {
}
type ResourceView struct {
ID uint `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `json:"created_at"`
Tags []TagView `json:"tags"`
Image *ImageView `json:"image"`
Author UserView `json:"author"`
ID uint `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `json:"created_at"`
ReleaseDate *time.Time `json:"release_date,omitempty"`
Tags []TagView `json:"tags"`
Image *ImageView `json:"image"`
Author UserView `json:"author"`
}
type ResourceDetailView struct {
@@ -47,6 +49,7 @@ type ResourceDetailView struct {
Links []Link `json:"links"`
Article string `json:"article"`
CreatedAt time.Time `json:"createdAt"`
ReleaseDate *time.Time `json:"releaseDate,omitempty"`
Tags []TagView `json:"tags"`
Images []ImageView `json:"images"`
Files []FileView `json:"files"`
@@ -81,12 +84,13 @@ func (r *Resource) ToView() ResourceView {
}
return ResourceView{
ID: r.ID,
Title: r.Title,
CreatedAt: r.CreatedAt,
Tags: tags,
Image: image,
Author: r.User.ToView(),
ID: r.ID,
Title: r.Title,
CreatedAt: r.CreatedAt,
ReleaseDate: r.ReleaseDate,
Tags: tags,
Image: image,
Author: r.User.ToView(),
}
}
@@ -115,6 +119,7 @@ func (r *Resource) ToDetailView() ResourceDetailView {
Links: r.Links,
Article: r.Article,
CreatedAt: r.CreatedAt,
ReleaseDate: r.ReleaseDate,
Tags: tags,
Images: images,
Files: files,

View File

@@ -30,6 +30,7 @@ type ResourceParams struct {
Title string `json:"title" binding:"required"`
AlternativeTitles []string `json:"alternative_titles"`
Links []model.Link `json:"links"`
ReleaseDate string `json:"release_date"`
Tags []uint `json:"tags"`
Article string `json:"article"`
Images []uint `json:"images"`
@@ -101,11 +102,20 @@ func CreateResource(uid uint, params *ResourceParams) (uint, error) {
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{
Title: params.Title,
AlternativeTitles: params.AlternativeTitles,
Article: params.Article,
Links: params.Links,
ReleaseDate: date,
Images: images,
Tags: tags,
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.AlternativeTitles = params.AlternativeTitles
r.Article = params.Article
r.Links = params.Links
r.ReleaseDate = date
r.Gallery = gallery
r.GalleryNsfw = nsfw
r.Characters = characters