mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-02-08 08:04:19 +01:00
Merge branch 'dev' into feature/list-your-product-ui
This commit is contained in:
@@ -18,6 +18,7 @@ const { marked } = require('marked');
|
||||
const { resolveImgObjectToUrl } = require('../../../utils/resolveImageUrl');
|
||||
const { ImageInput } = require('./misc');
|
||||
const { deleteImage } = require('../../../services/imageUpload.service');
|
||||
const { logError } = require('../../../utils/logger');
|
||||
|
||||
|
||||
const POST_TYPE = enumType({
|
||||
@@ -435,97 +436,129 @@ const createStory = extendType({
|
||||
let coverImage = null
|
||||
let bodyImageIds = []
|
||||
|
||||
// Edit story
|
||||
if (id) {
|
||||
const oldPost = await prisma.story.findFirst({
|
||||
where: { id },
|
||||
select: {
|
||||
user_id: true,
|
||||
is_published: true,
|
||||
cover_image_id: true,
|
||||
body_image_ids: true
|
||||
}
|
||||
})
|
||||
was_published = oldPost.is_published;
|
||||
if (user.id !== oldPost.user_id) throw new ApolloError("Not post author")
|
||||
|
||||
// Body images
|
||||
bodyImageIds = await getHostedImageIdsFromBody(body, oldPost.body_image_ids)
|
||||
|
||||
// Old cover image is found
|
||||
if (oldPost.cover_image_id) {
|
||||
const oldCoverImage = await prisma.hostedImage.findFirst({
|
||||
where: {
|
||||
id: oldPost.cover_image_id
|
||||
try {
|
||||
if (id) {
|
||||
const oldPost = await prisma.story.findFirst({
|
||||
where: { id },
|
||||
select: {
|
||||
user_id: true,
|
||||
is_published: true,
|
||||
cover_image_id: true,
|
||||
body_image_ids: true
|
||||
}
|
||||
})
|
||||
was_published = oldPost.is_published;
|
||||
if (user.id !== oldPost.user_id) throw new ApolloError("Not post author")
|
||||
|
||||
// New cover image
|
||||
if (cover_image?.id && cover_image.id !== oldCoverImage?.provider_image_id) {
|
||||
await deleteImage(oldCoverImage.id)
|
||||
coverImage = await addCoverImage(cover_image.id)
|
||||
// Body images
|
||||
bodyImageIds = await getHostedImageIdsFromBody(body, oldPost.body_image_ids)
|
||||
|
||||
// Old cover image is found
|
||||
if (oldPost.cover_image_id) {
|
||||
const oldCoverImage = await prisma.hostedImage.findFirst({
|
||||
where: {
|
||||
id: oldPost.cover_image_id
|
||||
}
|
||||
})
|
||||
|
||||
// New cover image
|
||||
if (cover_image?.id && cover_image.id !== oldCoverImage?.provider_image_id) {
|
||||
await deleteImage(oldCoverImage.id)
|
||||
coverImage = await addCoverImage(cover_image.id)
|
||||
} else {
|
||||
coverImage = oldCoverImage
|
||||
}
|
||||
} else {
|
||||
coverImage = oldCoverImage
|
||||
// No old image found and new cover image
|
||||
if (cover_image?.id) {
|
||||
coverImage = await addCoverImage(cover_image.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unused body images
|
||||
const unusedImagesIds = oldPost.body_image_ids.filter(x => !bodyImageIds.includes(x));
|
||||
unusedImagesIds.map(async i => await deleteImage(i))
|
||||
|
||||
} else {
|
||||
// No old image found and new cover image
|
||||
// Body images
|
||||
bodyImageIds = await getHostedImageIdsFromBody(body)
|
||||
|
||||
// New story and new cover image
|
||||
if (cover_image?.id) {
|
||||
coverImage = await addCoverImage(cover_image.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unused body images
|
||||
const unusedImagesIds = oldPost.body_image_ids.filter(x => !bodyImageIds.includes(x));
|
||||
unusedImagesIds.map(async i => await deleteImage(i))
|
||||
|
||||
} else {
|
||||
// Body images
|
||||
bodyImageIds = await getHostedImageIdsFromBody(body)
|
||||
|
||||
// New story and new cover image
|
||||
if (cover_image?.id) {
|
||||
coverImage = await addCoverImage(cover_image.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Preprocess & insert
|
||||
const htmlBody = marked.parse(body);
|
||||
const excerpt = htmlBody
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.slice(0, 120)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/"/g, '"')
|
||||
;
|
||||
// Preprocess & insert
|
||||
const htmlBody = marked.parse(body);
|
||||
const excerpt = htmlBody
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.slice(0, 120)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/"/g, '"')
|
||||
;
|
||||
|
||||
|
||||
const coverImageRel = coverImage ? {
|
||||
cover_image_rel: {
|
||||
connect:
|
||||
{
|
||||
id: coverImage ? coverImage.id : null
|
||||
const coverImageRel = coverImage ? {
|
||||
cover_image_rel: {
|
||||
connect:
|
||||
{
|
||||
id: coverImage ? coverImage.id : null
|
||||
}
|
||||
}
|
||||
} : {}
|
||||
|
||||
if (id) {
|
||||
await prisma.story.update({
|
||||
where: { id },
|
||||
data: {
|
||||
tags: {
|
||||
set: []
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return prisma.story.update({
|
||||
where: { id },
|
||||
data: {
|
||||
title,
|
||||
body,
|
||||
cover_image: '',
|
||||
excerpt,
|
||||
is_published: was_published || is_published,
|
||||
tags: {
|
||||
connectOrCreate:
|
||||
tags.map(tag => {
|
||||
tag = tag.toLowerCase().trim();
|
||||
return {
|
||||
where: {
|
||||
title: tag,
|
||||
},
|
||||
create: {
|
||||
title: tag
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
body_image_ids: bodyImageIds,
|
||||
...coverImageRel
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
logError(error)
|
||||
throw new ApolloError("Unexpected error happened...")
|
||||
})
|
||||
}
|
||||
} : {}
|
||||
|
||||
if (id) {
|
||||
await prisma.story.update({
|
||||
where: { id },
|
||||
data: {
|
||||
tags: {
|
||||
set: []
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
return prisma.story.update({
|
||||
where: { id },
|
||||
return prisma.story.create({
|
||||
data: {
|
||||
title,
|
||||
body,
|
||||
cover_image: '',
|
||||
excerpt,
|
||||
is_published: was_published || is_published,
|
||||
is_published,
|
||||
tags: {
|
||||
connectOrCreate:
|
||||
tags.map(tag => {
|
||||
@@ -540,42 +573,24 @@ const createStory = extendType({
|
||||
}
|
||||
})
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: user.id,
|
||||
}
|
||||
},
|
||||
body_image_ids: bodyImageIds,
|
||||
...coverImageRel
|
||||
}
|
||||
}).catch(error => {
|
||||
logError(error)
|
||||
throw new ApolloError("Unexpected error happened...")
|
||||
})
|
||||
}
|
||||
|
||||
return await prisma.story.create({
|
||||
data: {
|
||||
title,
|
||||
body,
|
||||
cover_image: '',
|
||||
excerpt,
|
||||
is_published,
|
||||
tags: {
|
||||
connectOrCreate:
|
||||
tags.map(tag => {
|
||||
tag = tag.toLowerCase().trim();
|
||||
return {
|
||||
where: {
|
||||
title: tag,
|
||||
},
|
||||
create: {
|
||||
title: tag
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: user.id,
|
||||
}
|
||||
},
|
||||
body_image_ids: bodyImageIds,
|
||||
...coverImageRel
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
logError(error)
|
||||
throw new ApolloError("Unexpected error happened...")
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ const jose = require('jose');
|
||||
const { JWT_SECRET } = require('../../utils/consts');
|
||||
const { generatePrivateKey, getPublicKey } = require('../../utils/nostr-tools');
|
||||
const { getUserByPubKey } = require('../../auth/utils/helperFuncs');
|
||||
const { logError } = require('../../utils/logger');
|
||||
|
||||
|
||||
|
||||
@@ -28,10 +29,18 @@ const loginHandler = async (req, res) => {
|
||||
|
||||
if (action === 'link' && user_token) {
|
||||
try {
|
||||
const { payload } = await jose.jwtVerify(user_token, Buffer.from(JWT_SECRET), {
|
||||
algorithms: ['HS256'],
|
||||
})
|
||||
const user_id = payload.user_id;
|
||||
|
||||
let user_id;
|
||||
|
||||
try {
|
||||
const { payload } = await jose.jwtVerify(user_token, Buffer.from(JWT_SECRET), {
|
||||
algorithms: ['HS256'],
|
||||
})
|
||||
user_id = payload.user_id;
|
||||
} catch (error) {
|
||||
return res.status(400).json({ status: 'ERROR', reason: "Invalid user_token" })
|
||||
}
|
||||
|
||||
const existingKeys = await prisma.userKey.findMany({ where: { user_id }, select: { key: true } });
|
||||
|
||||
if (existingKeys.length >= 3)
|
||||
@@ -55,7 +64,8 @@ const loginHandler = async (req, res) => {
|
||||
.json({ status: "OK" })
|
||||
|
||||
} catch (error) {
|
||||
return res.status(400).json({ status: 'ERROR', reason: 'Invalid User Token' })
|
||||
logError(error)
|
||||
return res.status(500).json({ status: 'ERROR', reason: 'Unexpected error happened' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +75,7 @@ const loginHandler = async (req, res) => {
|
||||
const user = await getUserByPubKey(key)
|
||||
if (user === null) {
|
||||
|
||||
// Check if user had a previous account using this wallet
|
||||
|
||||
// Check if user had a previous account using this wallet
|
||||
const oldAccount = await prisma.user.findFirst({
|
||||
where: {
|
||||
pubKey: key
|
||||
@@ -136,7 +145,8 @@ const loginHandler = async (req, res) => {
|
||||
return res.status(200).json({ status: "OK" })
|
||||
|
||||
} catch (error) {
|
||||
return res.status(400).json({ status: 'ERROR', reason: 'Unexpected error happened, please try again' })
|
||||
logError(error)
|
||||
return res.status(500).json({ status: 'ERROR', reason: 'Unexpected error happened, please try again' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
api/utils/logger.js
Normal file
9
api/utils/logger.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
function logError(error) {
|
||||
console.log("Unexpected Error: ");
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
logError
|
||||
}
|
||||
@@ -9,8 +9,39 @@ interface Props {
|
||||
export default function PrizesSection({ prizes }: Props) {
|
||||
return (
|
||||
<div>
|
||||
<h2 className='text-body1 font-bolder text-gray-900 mb-16'>Prizes</h2>
|
||||
<div className={styles.grid}>
|
||||
<h2 className='text-body1 font-bolder text-gray-900 mb-16'>{data.tracks.length > 0 ? "Prizes & Tracks" : "Prizes"}</h2>
|
||||
<div className="flex flex-col gap-16">
|
||||
{data.tracks.map((track, trackNumber) => <div key={track.id}
|
||||
className="bg-gray-50 rounded-16 border-2 border-gray-100 p-16 md:p-40"
|
||||
>
|
||||
<div className="flex justify-between gap-24 flex-col md:flex-row">
|
||||
<div className='flex flex-col items-start gap-8 max-w-[400px]'>
|
||||
<img src={track.image} alt={`${track.title} track prize`} className='h-[64px]' />
|
||||
<h3 className="text-body2 text-gray-900 font-bolder">{track.title}</h3>
|
||||
<p className="text-body4 text-gray-500">{track.description}</p>
|
||||
</div>
|
||||
<div className={`text-right ${styles.prizes}`}>
|
||||
{/* One Prize */}
|
||||
{track.prizes.length === 1 &&
|
||||
<div>
|
||||
<h4 className='text-[32px]'>{track.prizes[0].title}</h4>
|
||||
<p className='text-[118px]' data-attr={trackNumber + 1}>{track.prizes[0].amount}</p>
|
||||
</div>
|
||||
}
|
||||
{/* Four Prizez */}
|
||||
{track.prizes.length === 4 &&
|
||||
<div className='flex justify-end flex-wrap gap-40'>
|
||||
{track.prizes.map((prize, idx) => <div key={prize.id} className='first:w-full'>
|
||||
<h4 className={`${idx === 0 ? "text-h2" : "text-body2"}`}>{prize.title}</h4>
|
||||
<p className={`${idx === 0 ? "text-[48px]" : "text-[36px]"}`} data-attr={trackNumber + 1}>{prize.amount}</p>
|
||||
</div>)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>)}
|
||||
</div>
|
||||
{/* <div className={styles.grid}>
|
||||
{prizes.map((prize, idx) => <div
|
||||
key={idx}
|
||||
className='bg-gray-50 rounded-16 py-24 px-32'>
|
||||
@@ -20,7 +51,82 @@ export default function PrizesSection({ prizes }: Props) {
|
||||
<p className="text-h1 text-green-500">{prize.amount}</p>
|
||||
</div>
|
||||
</div>)}
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const data = {
|
||||
prizes: [],
|
||||
tracks: [
|
||||
{
|
||||
id: 1,
|
||||
title: "Grand Champion",
|
||||
description: "Our Grand Champion, a.k.a “The Legend of Lightning” will be the best in show, la créme de la créme. Every project entered in the tournament will be eligible for this award, no matter what track they choose.",
|
||||
image: "https://s3-alpha-sig.figma.com/img/33fa/68dc/1015f7806d8706cbb29b057f85482755?Expires=1664755200&Signature=QdesbJJcLG84k-SudRv9ah-tVSf~zv4NZKU1EQM9cz-L7qZ1crx7awSVBFZdP~p4R7h1FsUqQfSNHsOPQOKTRiWOL~mpKLe6SAlKhdeqrm8RCNmnhNHpMOxJrCGAsJ7vQDkUKFw9VsJjufTjtEgLHN-EWH5L~RvNHKa06f6rRyiMeRl5HCu9JWT5Spjb0zK7IrU2gT7G~Dw0FTdbE35uxCbN9pU-XuPLbqmAIsPBR-gV4uuf21NBapFOLFDazi-tDzIJO--vH6C4RjuI-i3sl1WV75-SM0DW9MVNBvXiWfPrtGXbNd379xJXQoCBVxv4qzl3YkdoxFUG1-uwKTrVaA__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA",
|
||||
prizes: [
|
||||
{
|
||||
id: 1,
|
||||
title: "the legend",
|
||||
amount: "1 BTC"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Bitcoin Adoption Track",
|
||||
description: "On-chain applications have been the bed rock of bitcoin’s explosive growth and adoption over the last decade. Slowly, steadily, and securely, bitcoin’s base layer always seeks to evolve and improve.",
|
||||
image: "https://s3-alpha-sig.figma.com/img/cb90/77b4/5ea853a671d0cb1c64bde10dd8955d39?Expires=1664755200&Signature=aXtIhKJg58wRTQlJIGWxpfCN2hxJx8L0~8Hu5aH8LKUVAYrxSV5Tvvxevx9xDnf-RpjTVfB6D7RKuVQjfIiftB4Ym80oOlW9tNzYUo991cJhdYnqaGzJ6Ht2kF7NHmxbiY5RUMYj8bGf2AF1A2a7wuW~DaqHyLQ0s2sszwH2EAv31QTH1DAOO97pQzQ5asas7qGjARWh45QEfw6F8e~6iq3UWHXtIcJ0HMJO4q3ONhsMkuC6XQNfAmWTRwKb3tPZ79oehWgDeyOMGQkRS0uaal~6fNkheEN5DuRBH2dbXtqB6va0PJCTB1l8P558HXhKQjHXRLPPReIci72jPuTzdA__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA",
|
||||
prizes: [
|
||||
{
|
||||
id: 2,
|
||||
title: " 1st",
|
||||
amount: "$5k"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: " 2nd",
|
||||
amount: "$2.5k"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: " 3rd",
|
||||
amount: "$1.5k"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: " Design",
|
||||
amount: "$1k"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Building for Africa track",
|
||||
description: "With ~1.4bn inhabitants, Africa is ripe for bitcoin adoption. In partnership with ABC 22, this track seeks to encourage makers to build solutions that are local to the African continent.",
|
||||
image: "https://s3-alpha-sig.figma.com/img/c306/f172/7ce7befa9414372e6d0ede739be46de8?Expires=1664755200&Signature=BBfOTJzk7Si7zs9dOBhTdIhoKCvUDxAr6Do0wCZaIq9PD2Jcfxu3ANbiogzihC5O2Rwz3sKsajsRCd8eSs8HGrHrQh89SfNIl0~MYjMz12yWpsc1vC5M5hmXH~VQzCTOWsSki9BimcpCu0IOWfJFjY-p0rlo8UFhdDe56DiRUOSW0pAm5UxTstzOew6X015xA3qQWwUIea2JAtlsI5RqMQMRB-QlaKFlQvYHBU6YzLUNTuTn4MfOd-1oZXKtDArubYnSrJb2rJAXqccxgsXceDl8jq8HXKwkBR95-sG3UDZB7q7qb1Nk3HlsDtirGNlOjLx~vDKpOuyIk5ufAkdJmQ__&Key-Pair-Id=APKAINTVSUGEWH5XD5UA",
|
||||
prizes: [
|
||||
{
|
||||
id: 6,
|
||||
title: " 1st",
|
||||
amount: "$5k"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: " 2nd",
|
||||
amount: "$2.5k"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: " 3rd",
|
||||
amount: "$1.5k"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: " Design",
|
||||
amount: "$1k"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,32 @@
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Luckiest+Guy&display=swap");
|
||||
|
||||
.prizes {
|
||||
font-family: "Luckiest Guy", cursive;
|
||||
h4 {
|
||||
color: white;
|
||||
-webkit-text-stroke: 1px black;
|
||||
}
|
||||
|
||||
@include gt-md {
|
||||
h4 {
|
||||
-webkit-text-stroke: 0.06em black;
|
||||
}
|
||||
}
|
||||
|
||||
p[data-attr="1"] {
|
||||
color: #fb923c;
|
||||
}
|
||||
|
||||
p[data-attr="2"] {
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
p[data-attr="3"] {
|
||||
color: #3b82f6;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
font-family: "Luckiest Guy", cursive;
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user