Added fetch cache and revalidations
Some checks failed
Music Collection CI Workflow / test (./backend) (push) Successful in 30s
Music Collection CI Workflow / test (./frontend) (push) Successful in 36s
Music Collection CI Workflow / build-and-push-images (./backend/Dockerfile, git.anatid.net/tabris/music-collection-backend, ./backend) (push) Successful in 52s
Music Collection CI Workflow / deploy (push) Has been cancelled
Music Collection CI Workflow / build-and-push-images (./frontend/Dockerfile, git.anatid.net/tabris/music-collection-frontend, ./frontend) (push) Has been cancelled

This commit is contained in:
Phill Pover 2025-04-07 07:33:35 +01:00
parent 62acd6a2d0
commit 4fefb473d5
3 changed files with 55 additions and 12 deletions

View File

@ -1,9 +1,31 @@
'use server' 'use server'
import { revalidateTag } from 'next/cache'
export async function revalidateAlbums() {
revalidateTag('albums')
}
export async function revalidateAlbum() {
revalidateTag('album')
}
export async function revalidateSongs() {
revalidateTag('songs')
}
export async function revalidateSong() {
revalidateTag('song')
}
export async function getAlbums() { export async function getAlbums() {
return fetch('https://api.anatid.net/album', { return fetch('https://api.anatid.net/album', {
method: "GET", method: "GET",
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" },
next: {
revalidate: 300,
tags: ['albums']
}
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
@ -17,7 +39,11 @@ export async function getAlbums() {
export async function getAlbum(id: number) { export async function getAlbum(id: number) {
return fetch(`https://api.anatid.net/album/${id}`, { return fetch(`https://api.anatid.net/album/${id}`, {
method: "GET", method: "GET",
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" },
next: {
revalidate: 300,
tags: ['album']
}
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
@ -95,7 +121,11 @@ export async function deleteAlbum(id: number) {
export async function getSongs() { export async function getSongs() {
return fetch("https://api.anatid.net/song/", { return fetch("https://api.anatid.net/song/", {
method: "GET", method: "GET",
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" },
next: {
revalidate: 300,
tags: ['songs']
}
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();
@ -109,7 +139,11 @@ export async function getSongs() {
export async function getSong(id: number) { export async function getSong(id: number) {
return fetch(`https://api.anatid.net/song/${id}`, { return fetch(`https://api.anatid.net/song/${id}`, {
method: "GET", method: "GET",
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" },
next: {
revalidate: 300,
tags: ['song']
}
}).then(response => { }).then(response => {
if (response.ok) { if (response.ok) {
return response.json(); return response.json();

View File

@ -5,7 +5,7 @@ import { useParams } from 'next/navigation'
import { Album } from '@/entities/album.entity'; import { Album } from '@/entities/album.entity';
import { Song } from '@/entities/song.entity'; import { Song } from '@/entities/song.entity';
import { TimeUtils } from '@/utils/time.util'; import { TimeUtils } from '@/utils/time.util';
import { createSong, deleteSong, getAlbum, getSong, updateSong } from '@/app/actions'; import { createSong, deleteSong, getAlbum, getSong, updateSong, revalidateAlbum } from '@/app/actions';
import Button from 'react-bootstrap/Button'; import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal'; import Modal from 'react-bootstrap/Modal';
import { IconButton } from '@mui/material'; import { IconButton } from '@mui/material';
@ -29,6 +29,7 @@ export default function Page() {
useEffect(() => { useEffect(() => {
async function fetchAlbum(albumId: string) { async function fetchAlbum(albumId: string) {
try { try {
revalidateAlbum();
const data = await getAlbum(parseInt(albumId)); const data = await getAlbum(parseInt(albumId));
setAlbum(data); setAlbum(data);
} catch (error) { } catch (error) {
@ -49,6 +50,7 @@ export default function Page() {
} else { } else {
await updateSong(formData); await updateSong(formData);
} }
revalidateAlbum();
const data = await getAlbum(parseInt(albumId)); const data = await getAlbum(parseInt(albumId));
setAlbum(data); setAlbum(data);
} catch (error) { } catch (error) {
@ -83,6 +85,7 @@ export default function Page() {
} catch (error) { } catch (error) {
console.error(`Error getting song with ID ${songId}:`, error); console.error(`Error getting song with ID ${songId}:`, error);
} }
revalidateAlbum();
const data = await getAlbum(parseInt(albumId)); const data = await getAlbum(parseInt(albumId));
setAlbum(data); setAlbum(data);
} else { } else {
@ -98,6 +101,7 @@ export default function Page() {
} catch (error) { } catch (error) {
console.error(`Error deleting song with ID ${songId}:`, error); console.error(`Error deleting song with ID ${songId}:`, error);
} }
revalidateAlbum();
const data = await getAlbum(parseInt(albumId)); const data = await getAlbum(parseInt(albumId));
setAlbum(data); setAlbum(data);
} else { } else {
@ -116,7 +120,7 @@ export default function Page() {
<em>({album.genre})</em> <em>({album.genre})</em>
</div> </div>
<IconButton aria-label="Add Album" size="large" onClick={handleCreate}> <IconButton aria-label="Add Album" size="large" onClick={handleCreate}>
<AddCircleOutline fontSize="inherit" color="success"/> <AddCircleOutline fontSize="large" color="success"/>
</IconButton> </IconButton>
<table className="u-full-width"> <table className="u-full-width">
<thead> <thead>
@ -135,10 +139,10 @@ export default function Page() {
<td>{TimeUtils.fancyTimeFormat(song.duration)}</td> <td>{TimeUtils.fancyTimeFormat(song.duration)}</td>
<td> <td>
<IconButton aria-label="Edit Song" size="small" song-id={song.id.toString()} onClick={handleEdit}> <IconButton aria-label="Edit Song" size="small" song-id={song.id.toString()} onClick={handleEdit}>
<Edit fontSize="inherit" color="success"/> <Edit fontSize="large" color="success"/>
</IconButton> </IconButton>
<IconButton aria-label="Delete Song" size="small" song-id={song.id.toString()} onClick={handleDelete}> <IconButton aria-label="Delete Song" size="small" song-id={song.id.toString()} onClick={handleDelete}>
<Delete fontSize="inherit" color="error"/> <Delete fontSize="large" color="error"/>
</IconButton> </IconButton>
</td> </td>
</tr> </tr>

View File

@ -3,7 +3,7 @@
import { FormEvent, MouseEvent, useState, useEffect } from 'react'; import { FormEvent, MouseEvent, useState, useEffect } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { Album } from '@/entities/album.entity'; import { Album } from '@/entities/album.entity';
import { createAlbum, deleteAlbum, getAlbum, getAlbums, updateAlbum } from '@/app/actions'; import { createAlbum, deleteAlbum, getAlbum, getAlbums, updateAlbum, revalidateAlbums } from '@/app/actions';
import Button from 'react-bootstrap/Button'; import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal'; import Modal from 'react-bootstrap/Modal';
import { IconButton } from '@mui/material'; import { IconButton } from '@mui/material';
@ -24,6 +24,7 @@ export default function Page() {
useEffect(() => { useEffect(() => {
async function fetchAlbums() { async function fetchAlbums() {
try { try {
revalidateAlbums();
const data = await getAlbums(); const data = await getAlbums();
setAlbums(data); setAlbums(data);
} catch (error) { } catch (error) {
@ -44,6 +45,8 @@ export default function Page() {
} else { } else {
await updateAlbum(formData); await updateAlbum(formData);
} }
revalidateAlbums();
revalidateAlbums();
const data = await getAlbums(); const data = await getAlbums();
setAlbums(data); setAlbums(data);
} catch (error) { } catch (error) {
@ -76,6 +79,7 @@ export default function Page() {
} catch (error) { } catch (error) {
console.error(`Error getting album with ID ${id}:`, error); console.error(`Error getting album with ID ${id}:`, error);
} }
revalidateAlbums();
const data = await getAlbums(); const data = await getAlbums();
setAlbums(data); setAlbums(data);
} else { } else {
@ -91,6 +95,7 @@ export default function Page() {
} catch (error) { } catch (error) {
console.error(`Error deleting album with ID ${id}:`, error); console.error(`Error deleting album with ID ${id}:`, error);
} }
revalidateAlbums();
const data = await getAlbums(); const data = await getAlbums();
setAlbums(data); setAlbums(data);
} else { } else {
@ -104,7 +109,7 @@ export default function Page() {
<> <>
<div className="container"> <div className="container">
<IconButton aria-label="Add Album" size="large" onClick={handleCreate}> <IconButton aria-label="Add Album" size="large" onClick={handleCreate}>
<AddCircleOutline fontSize="inherit" color="success"/> <AddCircleOutline fontSize="large" color="success"/>
</IconButton> </IconButton>
<table className="u-full-width"> <table className="u-full-width">
<thead> <thead>
@ -133,10 +138,10 @@ export default function Page() {
</td> </td>
<td> <td>
<IconButton aria-label="Edit Album" size="small" album-id={album.id.toString()} onClick={handleEdit}> <IconButton aria-label="Edit Album" size="small" album-id={album.id.toString()} onClick={handleEdit}>
<Edit fontSize="inherit" color="success"/> <Edit fontSize="large" color="success"/>
</IconButton> </IconButton>
<IconButton aria-label="Delete Album" size="small" album-id={album.id.toString()} onClick={handleDelete}> <IconButton aria-label="Delete Album" size="small" album-id={album.id.toString()} onClick={handleDelete}>
<Delete fontSize="inherit" color="error"/> <Delete fontSize="large" color="error"/>
</IconButton> </IconButton>
</td> </td>
</tr> </tr>