mirror of
https://github.com/wisplite/raster.git
synced 2026-05-01 06:32:44 -05:00
small updates, for some reason it thinks every file is modified
This commit is contained in:
@@ -18,7 +18,7 @@ func main() {
|
||||
|
||||
// Configure CORS middleware
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:3000", "http://localhost:5173"},
|
||||
AllowOrigins: []string{"http://localhost:3000", "http://localhost:5173", "http://192.168.1.130:5173"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Red+Hat+Mono:ital,wght@0,300..700;1,300..700&family=Red+Hat+Text:ital,wght@0,300..700;1,300..700&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Red+Hat+Display:ital,wght@0,300..900;1,300..900&family=Red+Hat+Mono:ital,wght@0,300..700;1,300..700&family=Red+Hat+Text:ital,wght@0,300..700;1,300..700&display=swap"
|
||||
rel="stylesheet">
|
||||
<title>Raster</title>
|
||||
</head>
|
||||
|
||||
Generated
+10
@@ -75,6 +75,7 @@
|
||||
"integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
@@ -2115,6 +2116,7 @@
|
||||
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -2125,6 +2127,7 @@
|
||||
"integrity": "sha512-xG7xaBMJCpcK0RpN8jDbAACQo54ycO6h4dSSmgv8+fu6ZIAdANkx/WsawASUjVXYfy+J9AbUpRMNNEsXCDfDBQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
@@ -2156,6 +2159,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -2263,6 +2267,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001735",
|
||||
"electron-to-chromium": "^1.5.204",
|
||||
@@ -2528,6 +2533,7 @@
|
||||
"integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -3492,6 +3498,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -3552,6 +3559,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -3561,6 +3569,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
||||
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
@@ -3993,6 +4002,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
|
||||
"integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function CreateRootUser() {
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error creating root user: " + data.error)
|
||||
} else {
|
||||
const rootResponse = await fetch(`${getServerUrl()}/api/user/setRootUser`, {
|
||||
method: 'POST',
|
||||
@@ -34,7 +34,7 @@ export default function CreateRootUser() {
|
||||
})
|
||||
const rootData = await rootResponse.json()
|
||||
if (rootData.error) {
|
||||
showError(rootData.error)
|
||||
showError("Error setting root user: " + rootData.error)
|
||||
} else {
|
||||
navigate('/gallery')
|
||||
showSuccess('Root user created successfully')
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function Login() {
|
||||
navigate('/gallery')
|
||||
})
|
||||
.catch((error) => {
|
||||
showError(error.message)
|
||||
showError("Error logging in: " + error.message)
|
||||
})
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -10,11 +10,11 @@ export default function NavBar({ path }) {
|
||||
<div className="flex flex-row items-center justify-between h-[10vh] w-full px-6 py-2 border-b border-[#2B2B2B] shrink-0">
|
||||
<div className="flex flex-row items-center justify-start gap-2">
|
||||
{path.map((item, index) => (
|
||||
<div className="flex flex-row items-center justify-start gap-2 red-hat-mono">
|
||||
<div className="flex flex-row items-center justify-start gap-2 text-2xl red-hat-display">
|
||||
<Link to={`/${path.slice(0, index + 1).join('/')}`} key={item} className={`text-white ${index === path.length - 1 ? 'font-bold' : ''}`}>
|
||||
{decodeURIComponent(item)}
|
||||
</Link>
|
||||
{index !== path.length - 1 && <p className="text-white red-hat-mono">/</p>}
|
||||
{index !== path.length - 1 && <p className="text-white text-xl red-hat-display">/</p>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ export default function AlbumCreateModal({ open, onOpenChange, trigger, parentId
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error creating album: " + data.error)
|
||||
} else {
|
||||
onOpenChange(false)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function AlbumEditModal({ open, onOpenChange, trigger, id, startT
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error editing album: " + data.error)
|
||||
} else {
|
||||
onOpenChange(false)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function AlbumList({ currentAlbumName }) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-start h-min w-full bg-[#141414]">
|
||||
<div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4">
|
||||
<h1 className="text-xl font-bold text-white red-hat-mono">Albums</h1>
|
||||
<h1 className="text-xl font-bold text-white red-hat-display">Albums</h1>
|
||||
<PlusIcon className="w-6 h-6 cursor-pointer" color="white" onClick={() => setOpen(true)} />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-start gap-2 w-full px-6 flex-wrap">
|
||||
@@ -75,7 +75,7 @@ export default function AlbumList({ currentAlbumName }) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<p className="text-white red-hat-mono">{album.Title}</p>
|
||||
<p className="text-white red-hat-text">{album.Title}</p>
|
||||
<EllipsisVertical className="w-6 h-6 cursor-pointer" color="white" onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setEditingAlbum(album)
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function FilePicker({ currentAlbum, onFileSelect }) {
|
||||
const [media, setMedia] = useState(null)
|
||||
const [filePickerOpen, setFilePickerOpen] = useState(false)
|
||||
const { getAccessToken } = useAccount()
|
||||
const { showError } = useNotifier()
|
||||
const { showError, showInfo } = useNotifier()
|
||||
useEffect(() => {
|
||||
const getAlbum = async () => {
|
||||
const response = await fetch(`${getServerUrl()}/api/albums/getAlbumsInParent`, {
|
||||
@@ -28,7 +28,7 @@ export default function FilePicker({ currentAlbum, onFileSelect }) {
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error getting albums in parent: " + data.error)
|
||||
} else {
|
||||
setAlbums(data)
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export default function FilePicker({ currentAlbum, onFileSelect }) {
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error getting album info: " + data.error)
|
||||
} else {
|
||||
setSelectedAlbum(data)
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export default function FilePicker({ currentAlbum, onFileSelect }) {
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error getting media: " + data.error)
|
||||
} else {
|
||||
if (data.media.length === 0) {
|
||||
setMedia(null)
|
||||
@@ -99,7 +99,7 @@ export default function FilePicker({ currentAlbum, onFileSelect }) {
|
||||
|
||||
const data = await response.json()
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error getting album: " + data.error)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -11,21 +11,25 @@ export default function MediaList({ albumId, albumName }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [media, setMedia] = useState([])
|
||||
const [aspectRatios, setAspectRatios] = useState({})
|
||||
const { showError } = useNotifier()
|
||||
const { showError, showInfo } = useNotifier()
|
||||
const navigate = useNavigate()
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
const accessToken = getAccessToken()
|
||||
if (!accessToken || !albumId && albumName !== 'gallery') {
|
||||
return // assuming state isn't loaded yet
|
||||
}
|
||||
const getMedia = async () => {
|
||||
const response = await fetch(`${getServerUrl()}/api/media/getAllMediaInAlbum?albumId=${albumId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': getAccessToken(),
|
||||
'Authorization': accessToken,
|
||||
},
|
||||
})
|
||||
const data = await response.json()
|
||||
if (ignore) return
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error getting media in album: " + data.error)
|
||||
} else {
|
||||
setMedia(data.media)
|
||||
}
|
||||
@@ -51,7 +55,7 @@ export default function MediaList({ albumId, albumName }) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-start w-full bg-[#141414]">
|
||||
<div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4">
|
||||
<h1 className="text-xl font-bold text-white red-hat-mono">Media</h1>
|
||||
<h1 className="text-xl font-bold text-white red-hat-display">Media</h1>
|
||||
<Upload className="w-6 h-6 cursor-pointer" color="white" onClick={() => setOpen(true)} />
|
||||
</div>
|
||||
<MediaUploadModal
|
||||
@@ -89,7 +93,7 @@ export default function MediaList({ albumId, albumName }) {
|
||||
}}
|
||||
>
|
||||
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-b from-[#1A1A1A] via-transparent to-transparent flex items-start justify-start opacity-0 hover:opacity-100 transition-all duration-300">
|
||||
<p className="text-white text-sm truncate max-w-[100%] p-2 red-hat-mono">{item.Title}</p>
|
||||
<p className="text-white text-sm truncate max-w-[90%] p-2 red-hat-text">{item.Title}</p>
|
||||
<button className="text-white px-1 py-1 rounded-md absolute top-2 right-2 cursor-pointer z-50" onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setOpen(true)
|
||||
|
||||
@@ -22,3 +22,10 @@ body,
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.red-hat-display {
|
||||
font-family: "Red Hat Display", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export default function ImageViewer({ albumId, mediaId, token, title }) {
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center bg-black relative overflow-hidden h-full">
|
||||
<div className="flex-1 flex items-center justify-center bg-black relative overflow-hidden">
|
||||
<AuthImage
|
||||
src={src}
|
||||
token={token}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export default function MetadataPanel({ mediaItem }) {
|
||||
export default function MetadataPanel({ mediaItem, open, onOpenChange }) {
|
||||
if (!mediaItem) return <div className="w-80 bg-[#1A1A1A] h-full border-l border-[#2B2B2B] p-4 text-white">Loading...</div>
|
||||
|
||||
return (
|
||||
<div className="w-80 bg-[#1A1A1A] h-full border-l border-[#2B2B2B] p-6 text-white overflow-y-auto flex-shrink-0">
|
||||
<h2 className="text-xl font-bold mb-6 red-hat-mono break-words">{mediaItem.Title}</h2>
|
||||
<div className={`w-full md:w-80 bg-[#1A1A1A] h-72 md:h-full border-t md:border-t-0 md:border-l border-[#2B2B2B] p-6 text-white overflow-y-auto flex-shrink-0 ${open ? 'block' : 'hidden'}`}>
|
||||
<h2 className="text-xl font-bold mb-6 red-hat-display break-words">{mediaItem.Title}</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider">Details</h3>
|
||||
<div className="bg-[#222] rounded p-3 space-y-2 text-sm">
|
||||
<div className="bg-[#222] rounded p-3 space-y-2 text-sm red-hat-mono">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">Type</span>
|
||||
<span className="text-white">{mediaItem.Type || 'Unknown'}</span>
|
||||
@@ -25,7 +25,7 @@ export default function MetadataPanel({ mediaItem }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2 red-hat-mono">
|
||||
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wider">EXIF Data</h3>
|
||||
<p className="text-xs text-gray-500 italic">Metadata editing not yet implemented.</p>
|
||||
{/* Placeholder for EXIF data */}
|
||||
|
||||
@@ -5,7 +5,7 @@ import MetadataPanel from './components/MetadataPanel'
|
||||
import { useAccount } from '../contexts/useAccount'
|
||||
import { getServerUrl } from '../hooks/getConstants'
|
||||
import { useNotifier } from '../contexts/useNotifier'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
import { ArrowLeft, PanelRightClose, PanelRightOpen } from 'lucide-react'
|
||||
|
||||
export default function Viewer() {
|
||||
const { albumId, mediaId } = useParams()
|
||||
@@ -14,6 +14,7 @@ export default function Viewer() {
|
||||
const { getAccessToken } = useAccount()
|
||||
const { showError } = useNotifier()
|
||||
const [mediaItem, setMediaItem] = useState(location.state?.mediaItem || null)
|
||||
const [metadataPanelOpen, setMetadataPanelOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (mediaItem) return
|
||||
@@ -32,17 +33,17 @@ export default function Viewer() {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
showError(data.error)
|
||||
showError("Error getting media details: " + data.error)
|
||||
} else {
|
||||
const found = data.media.find(m => m.ID === mediaId)
|
||||
if (found) {
|
||||
setMediaItem(found)
|
||||
} else {
|
||||
showError("Media not found")
|
||||
showError("Media not found in album")
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
showError("Failed to fetch media details")
|
||||
showError("Failed to fetch media details: " + err.message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,24 +60,29 @@ export default function Viewer() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen w-full bg-[#141414]">
|
||||
<div className="flex items-center h-14 px-4 border-b border-[#2B2B2B] bg-[#141414] flex-shrink-0 gap-4">
|
||||
<button onClick={handleBack} className="text-gray-400 hover:text-white transition-colors p-1 cursor-pointer">
|
||||
<ArrowLeft size={20} />
|
||||
<div className="flex flex-col h-dvh w-full bg-[#141414]">
|
||||
<div className="flex items-center h-14 px-4 border-b border-[#2B2B2B] bg-[#141414] flex-shrink-0 gap-4 justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={handleBack} className="text-gray-400 hover:text-white transition-colors p-1 cursor-pointer">
|
||||
<ArrowLeft size={20} />
|
||||
</button>
|
||||
<span className="text-white font-medium truncate red-hat-text">
|
||||
{mediaItem ? mediaItem.Title : 'Loading...'}
|
||||
</span>
|
||||
</div>
|
||||
<button onClick={() => setMetadataPanelOpen(!metadataPanelOpen)} className="text-gray-400 hover:text-white transition-colors p-1 cursor-pointer">
|
||||
{metadataPanelOpen ? <PanelRightClose size={20} /> : <PanelRightOpen size={20} />}
|
||||
</button>
|
||||
<span className="text-white font-medium truncate red-hat-mono">
|
||||
{mediaItem ? mediaItem.Title : 'Loading...'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<div className="flex flex-col md:flex-row flex-1 overflow-hidden">
|
||||
<ImageViewer
|
||||
albumId={albumId === 'root' ? '' : albumId}
|
||||
mediaId={mediaId}
|
||||
token={getAccessToken()}
|
||||
title={mediaItem?.Title || ''}
|
||||
/>
|
||||
<MetadataPanel mediaItem={mediaItem} />
|
||||
<MetadataPanel mediaItem={mediaItem} open={metadataPanelOpen} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user