The problem is using the uploadBytes function, when I comment it out (as well as the imageRef as seen below) everything runs fine (except image upload obviously), but with it I get all these errors like this:
Access to XMLHttpRequest at [ firebase storage api url ] from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
// New info here:
I Think i've mostly wrangled this thing down, what i need to do is update the cors policy of the firestore bucket. it seems you use a json like cors.json below, and then use this command: gsutil cors set cors.json gs://<your app name, at top of firestore >.appspot.com
Unfortunately for me, when i run this, it cannot find the bucket, i'll let y'all know if i find something,
// end new info
The Vite config and firebase files are included, then the create.jsx file, where the error occurs is below.
vite.config.js
import { resolve } from 'path';
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
const root = resolve(__dirname, 'src');
const outDir = resolve(__dirname, 'dist');
// https://vitejs.dev/config/
export default defineConfig({
root,
plugins: [react()],
build: {
outDir,
emptyOutDir: true,
rollupOptions: {
input: {
main: resolve(root, "index.html"),
login: resolve(root, 'auth', "login.html"),
}
}
}
})
firebase.js
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: import.meta.env.VITE_PUBLIC_API_KEY,
authDomain: import.meta.env.VITE_PUBLIC_AUTH_DOMAIN,
projectId: import.meta.env.VITE_PUBLIC_PROJECT_ID,
storageBucket: import.meta.env.VITE_PUBLIC_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_PUBLIC_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_PUBLIC_APP_ID,
measurementId: import.meta.env.VITE_PUBLIC_MEASUREMENT_ID
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
//const analytics = getAnalytics(app);
export const auth = getAuth();
export const db = getFirestore(app);
// for image:
import { getStorage, ref, uploadBytes } from "firebase/storage"; // new
export const storage = getStorage(app)
create.jsx
import { auth, db } from "../utils/firebase"
import { useAuthState } from "react-firebase-hooks/auth"
import { useEffect, useState } from "react"
import { AiOutlineCloseCircle } from "react-icons/Ai";
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
// I need to upload the photo
// create reference
// create post, including reference
import { getStorage, ref, uploadBytes } from "firebase/storage"; // new
import { nanoid } from 'nanoid'
import { storage } from '../utils/firebase'
export default function Create(props){
const [post, setPost] = useState({imageSrc: undefined, title: "", description: ""})//maybe user id too?, probably fine without
const [preview, setPreview] = useState();
const [user,loading] = useAuthState(auth);
useEffect(() => {
if (!post.imageSrc) {
setPost({...post, imageSrc: undefined})
return
}
const objectUrl = URL.createObjectURL(post.imageSrc)
setPreview(objectUrl)
return () => URL.revokeObjectURL(objectUrl)
}, [post.imageSrc])
const handleFile = async (e) => {
if (!e.target.files || e.target.files.length === 0) {
setPost({...post, imageSrc: undefined})
return
}
setPost({...post, imageSrc: e.target.files[0]})
}
const submitPost = async (e) => {
e.preventDefault();
const collectionRef = collection(db, "posts");
// uploading image first:
const photoID = nanoid()
// const storage = getStorage();
const storageRef = ref(storage, `images/${photoID}`);
/////////////// PROBLEM IS HERE://///////////////////////////////////////////////////////
uploadBytes(storageRef, post.imageSrc).then(() => {
console.log("successful upload!")
})// this is the problem, idk if the imageSrc is the prob....
////////////// END PROBLEM HERE /////////////////////////////////////////////////////////
// creating image reference:
// normal stuff:
await addDoc(collectionRef, {
title: post.title,
description: post.description,
//imageRef: storageRef, // just connect the image reference here, but trough the firebase bucket// why no imageref...
timestamp: serverTimestamp(),
user: user.uid,
avatar: user.photoURL,
username: user.displayName,
});
props.handleCreating() // closes this when done
}
return (
<div className="mainCreate">
<AiOutlineCloseCircle onClick={() => props.handleCreating()} className="closeIcon"/>
<form onSubmit={submitPost}>
<div className="imageBox">
{post.imageSrc ? <img className="imagePreview" src={preview}/> : <h2>No Image</h2>}
{!post.imageSrc && <input type="file" onChange={handleFile}/>}
</div>
<textarea className="title" placeholder="Title" onChange={(e) => setPost({...post, title: e.target.value})} value={post.title} />
<p className={`lengthIndicator${post.title.length > 100 ? "Red" : ""}`}>{post.title.length}/100</p>
<textarea className="description" placeholder="Description" onChange={(e) => setPost({...post, description: e.target.value})} value={post.description} />
<p className={`lengthIndicator${post.description.length > 300 ? "Red" : ""}`}>{post.description.length}/300</p>
<button className="createButton" type="submit">Create</button>
</form>
</div>
)
}
the problem should be somewhere in the above, i've tried adding a proxy middleware, but i think i didn't figure it out, not sure, let me know if you need anythings else
not even sure exactly what the problem was, but all the cors and preflights stuff was a wild goose chase. Firebase handles all of this for us, my problems were in not having things represented where i thought they were. I also moved upload up earlier, to where image is selected.
firebase.js:
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
// for image:
import { getStorage} from "firebase/storage"; //, ref, uploadBytes } from "firebase/storage"; // new
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: import.meta.env.VITE_PUBLIC_API_KEY,
authDomain: import.meta.env.VITE_PUBLIC_AUTH_DOMAIN,
projectId: import.meta.env.VITE_PUBLIC_PROJECT_ID,
storageBucket: import.meta.env.VITE_PUBLIC_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_PUBLIC_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_PUBLIC_APP_ID,
measurementId: import.meta.env.VITE_PUBLIC_MEASUREMENT_ID
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// other way too, export..
//const analytics = getAnalytics(app);
const auth = getAuth();
// export const db = getFirestore(app);
const db = getFirestore(app); // firebase.firestore(); if do the other way
const storage = getStorage(app);// firebase.storage();
export { auth, db, storage };
create.jsx:
import { auth, db } from "../utils/firebase"
import { useAuthState } from "react-firebase-hooks/auth"
import { useEffect, useState } from "react"
import { AiOutlineCloseCircle } from "react-icons/Ai";
import { addDoc, collection, serverTimestamp } from "firebase/firestore";
// I need to upload the photo
// create reference
// create post, including reference
import { getStorage, ref, uploadBytes } from "firebase/storage"; // new
import { nanoid } from 'nanoid'
import { storage } from '../utils/firebase'
export default function Create(props){
const [post, setPost] = useState({imageSrc: undefined, title: "", description: ""})//maybe user id too?, probably fine without
const [preview, setPreview] = useState();
const [user,loading] = useAuthState(auth);
useEffect(() => {
if (!post.imageSrc) {
setPost({...post, imageSrc: undefined})
return
}
const objectUrl = URL.createObjectURL(post.imageSrc)
setPreview(objectUrl)
return () => URL.revokeObjectURL(objectUrl)
}, [post.imageSrc])
const handleFile = async (e) => {
if (!e.target.files || e.target.files.length === 0) {
setPost({...post, imageSrc: undefined})
return
}
///////// new
const storageRef = ref(storage);
const fileRef = ref(storageRef, `images/${nanoid()}`)
uploadBytes(fileRef, e.target.files[0]).then(() => {
console.log("successful upload!")
})
// setPost({...post, imageSrc: fileRef})
setPost({...post, imageSrc: e.target.files[0], imageRef: fileRef})
//console.log(post.imageSrc)
}
const submitPost = async (e) => {
if (!post.imageSrc) {
return;
}
e.preventDefault();
const collectionRef = collection(db, "posts");
await addDoc(collectionRef, {
title: post.title,
description: post.description,
imageRef: post.imageRef.fullPath, // this one is newer
timestamp: serverTimestamp(),
user: user.uid,
avatar: user.photoURL,
username: user.displayName,
});
props.handleCreating() // closes this when done
}
return (
<div className="mainCreate">
<AiOutlineCloseCircle onClick={() => props.handleCreating()} className="closeIcon"/>
<form onSubmit={submitPost}>
<div className="imageBox">
{post.imageSrc ? <img className="imagePreview" src={preview}/> : <h2>No Image</h2>}
{!post.imageSrc && <input type="file" onChange={handleFile}/>}
</div>
<textarea className="title" placeholder="Title" onChange={(e) => setPost({...post, title: e.target.value})} value={post.title} />
<p className={`lengthIndicator${post.title.length > 100 ? "Red" : ""}`}>{post.title.length}/100</p>
<textarea className="description" placeholder="Description" onChange={(e) => setPost({...post, description: e.target.value})} value={post.description} />
<p className={`lengthIndicator${post.description.length > 300 ? "Red" : ""}`}>{post.description.length}/300</p>
<button className="createButton" type="submit">Create</button>
</form>
</div>
)
}
hopefully this helps someone :)