feat: add file upload

feat: add db connection
This commit is contained in:
log101 2024-01-23 21:39:07 +03:00
parent efdf39ba9a
commit d5a267fead
6 changed files with 262 additions and 10 deletions

184
package-lock.json generated
View File

@ -19,9 +19,11 @@
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@vercel/blob": "^0.19.0", "@vercel/blob": "^0.19.0",
"@vercel/postgres-kysely": "^0.7.1",
"astro": "^4.1.2", "astro": "^4.1.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"kysely": "^0.27.2",
"lucide-react": "^0.309.0", "lucide-react": "^0.309.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -1083,6 +1085,14 @@
"node-pre-gyp": "bin/node-pre-gyp" "node-pre-gyp": "bin/node-pre-gyp"
} }
}, },
"node_modules/@neondatabase/serverless": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.7.2.tgz",
"integrity": "sha512-wU3WA2uTyNO7wjPs3Mg0G01jztAxUxzd9/mskMmtPwPTjf7JKWi9AW5/puOGXLxmZ9PVgRFeBVRVYq5nBPhsCg==",
"dependencies": {
"@types/pg": "8.6.6"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1499,6 +1509,24 @@
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
}, },
"node_modules/@types/node": {
"version": "20.11.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz",
"integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/pg": {
"version": "8.6.6",
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz",
"integrity": "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==",
"dependencies": {
"@types/node": "*",
"pg-protocol": "*",
"pg-types": "^2.2.0"
}
},
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.11", "version": "15.7.11",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
@ -1626,6 +1654,34 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/@vercel/postgres": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@vercel/postgres/-/postgres-0.7.1.tgz",
"integrity": "sha512-QTOQrzxkB+H37HrQ8n7uxuXQb9Q4/L8sibMuO98dNvkIv79iDmiy/jJ1rAcwn+D/6In3LnXAoH6S+XL6rpi8tQ==",
"dependencies": {
"@neondatabase/serverless": "0.7.2",
"bufferutil": "4.0.8",
"utf-8-validate": "6.0.3",
"ws": "8.14.2"
},
"engines": {
"node": ">=14.6"
}
},
"node_modules/@vercel/postgres-kysely": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@vercel/postgres-kysely/-/postgres-kysely-0.7.1.tgz",
"integrity": "sha512-HjWRWwreJTLfOP8Kio/naflXCqXY9mL7adn/Tv62XCjBwE6DNutU8LoDH072vt2A2Xqv0nmT794ORTidFLnvCg==",
"dependencies": {
"@vercel/postgres": "0.7.1"
},
"engines": {
"node": ">=14.6"
},
"peerDependencies": {
"kysely": "^0.24.2 || ^0.25.0 || ^0.26.0"
}
},
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
@ -2222,6 +2278,18 @@
"ieee754": "^1.2.1" "ieee754": "^1.2.1"
} }
}, },
"node_modules/bufferutil": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz",
"integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==",
"hasInstallScript": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -3845,6 +3913,14 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/kysely": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.2.tgz",
"integrity": "sha512-DmRvEfiR/NLpgsTbSxma2ldekhsdcd65+MNiKXyd/qj7w7X5e3cLkXxcj+MypsRDjPhHQ/CD5u3Eq1sBYzX0bw==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/lilconfig": { "node_modules/lilconfig": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
@ -5364,6 +5440,34 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
}, },
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-protocol": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@ -5585,6 +5689,41 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
}, },
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prebuild-install": { "node_modules/prebuild-install": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
@ -7319,6 +7458,11 @@
"node": ">=14.0" "node": ">=14.0"
} }
}, },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unherit": { "node_modules/unherit": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/unherit/-/unherit-3.0.1.tgz", "resolved": "https://registry.npmjs.org/unherit/-/unherit-3.0.1.tgz",
@ -7473,6 +7617,18 @@
"browserslist": ">= 4.21.0" "browserslist": ">= 4.21.0"
} }
}, },
"node_modules/utf-8-validate": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz",
"integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==",
"hasInstallScript": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -8037,6 +8193,34 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}, },
"node_modules/ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -21,9 +21,11 @@
"@types/react": "^18.2.47", "@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@vercel/blob": "^0.19.0", "@vercel/blob": "^0.19.0",
"@vercel/postgres-kysely": "^0.7.1",
"astro": "^4.1.2", "astro": "^4.1.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",
"kysely": "^0.27.2",
"lucide-react": "^0.309.0", "lucide-react": "^0.309.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -1,15 +1,70 @@
export const prerender = false export const prerender = false
import { put } from "@vercel/blob"
import type { Generated } from "kysely"
import type { APIRoute } from "astro" import type { APIRoute } from "astro"
import { createKysely } from "@vercel/postgres-kysely"
interface ContentTable {
id: Generated<number>
blob_url: string
loc: string
description: string
}
interface Database {
contents: ContentTable
}
export const POST: APIRoute = async ({ request }) => { export const POST: APIRoute = async ({ request }) => {
const data = await request.formData() const data = await request.formData()
console.log(data) const image = data.get("selected-photo") as File
const description = data.get("description")
const geolocation = data.get("geolocation")
console.log(image, description, geolocation)
if (!image || !geolocation) {
return new Response(null, {
status: 400,
statusText: "Image and geolocation are required fields"
})
}
const pos = geolocation.toString().split(",")
if (pos.length !== 2) {
return new Response(null, {
status: 400,
statusText: "Geolocation not correctly formatted"
})
}
const blob = await put(image.name, image, { access: "public", token: import.meta.env.BLOB_READ_WRITE_TOKEN })
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
const res = await db
.insertInto("contents")
.values({
blob_url: blob.url,
description: description?.toString() ?? "",
loc: `SRID=4326;POINT(${pos[0]} ${pos[1]})`
})
.returning("blob_url")
.executeTakeFirst()
if (res?.blob_url) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
message: "This was a POST!" message: "Succesfully added content"
}) })
) )
} else {
return new Response(null, {
status: 500,
statusText: "Error while saving data"
})
}
} }

View File

@ -72,6 +72,7 @@ import { Textarea } from '@/components/ui/textarea';
> >
</p> </p>
</div> </div>
<input name="geolocation" id="geolocation-input" hidden />
</div> </div>
<div> <div>
@ -81,7 +82,10 @@ import { Textarea } from '@/components/ui/textarea';
3. Fotoğraf Açıklaması 3. Fotoğraf Açıklaması
</h2> </h2>
<div class="grid w-full max-w-sm items-center gap-1.5 mt-4"> <div class="grid w-full max-w-sm items-center gap-1.5 mt-4">
<Textarea placeholder="Açıklamanızı buraya yazınız." name="desc" /> <Textarea
placeholder="Açıklamanızı buraya yazınız."
name="description"
/>
<p class="text-sm text-muted-foreground"> <p class="text-sm text-muted-foreground">
Bir açıklama girin, yüklediğiniz içeriğin üzerine bu metin Bir açıklama girin, yüklediğiniz içeriğin üzerine bu metin
görünecek. görünecek.

View File

@ -43,6 +43,8 @@ L.Control.GoToCurrentLocation = L.Control.extend({
locationButton.classList.add('custom-map-control-button'); locationButton.classList.add('custom-map-control-button');
locationButton.name = 'select-location-button'
locationButton.addEventListener('click', () => { locationButton.addEventListener('click', () => {
map.setView(currentLocationMarker.getLatLng(), 18); map.setView(currentLocationMarker.getLatLng(), 18);
}); });

View File

@ -36,10 +36,14 @@ L.Control.GoToCurrentLocation = L.Control.extend({
locationButton.classList.add('custom-map-control-button'); locationButton.classList.add('custom-map-control-button');
locationButton.addEventListener('click', (e) => { locationButton.type = 'button'
map.setView(currentLocationMarker.getLatLng(), 18);
e.stopPropagation() locationButton.addEventListener('click', (ev) => {
}); if (currentLocationMarker) {
map.setView(currentLocationMarker.getLatLng(), 12);
}
L.DomEvent.stopPropagation(ev)
})
return locationButton; return locationButton;
}, },
@ -115,5 +119,6 @@ map.on('click', (e) => {
const pos = targetLocationMarker.getLatLng() const pos = targetLocationMarker.getLatLng()
document.getElementById('coordinates').innerText = `${pos.lat}. Enlem, ${pos.lng}. Boylam` document.getElementById('coordinates').innerText = `${pos.lat}. Enlem, ${pos.lng}. Boylam`
document.getElementById('geolocation-input').value = `${pos.lat},${pos.lng}`
document.getElementById('location-selected-confirmation').innerText = "Konum seçildi, bir sonraki adıma geçebilirsiniz." document.getElementById('location-selected-confirmation').innerText = "Konum seçildi, bir sonraki adıma geçebilirsiniz."
}) })