Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added basic Community Page | Uses Profanity Api #99

Open
wants to merge 1 commit into
base: v2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
432 changes: 239 additions & 193 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"prettier": "^2.8.8",
"prettier-plugin-svelte": "^2.8.1",
"prettier-plugin-tailwindcss": "^0.3.0",
"prisma": "^4.15.0",
"prisma": "^5.13.0",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"tailwindcss": "^3.3.2",
Expand Down
9 changes: 9 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ model Game {
views Int @default(0)
}

model Community {
id String @id @unique @default(uuid())
name String

description String

embedURL String
}

model App {
id String @id @unique
name String
Expand Down
41 changes: 41 additions & 0 deletions src/lib/components/Box/CommunityBox.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts">
export let name: string;
export let description: string;
export let link: string;

let maxLength = 50;

// Truncate the description to the nearest sentence ending if it exceeds maxLength
function truncateDescription(description: string): string {
if (description.length <= maxLength) return description;

const endPunctuationIndex =
[
description.indexOf('?', maxLength),
description.indexOf('.', maxLength),
description.indexOf('!', maxLength)
]
.filter((index) => index !== -1)
.sort((a, b) => a - b)[0] ?? Infinity;

return endPunctuationIndex !== Infinity
? description.substring(0, endPunctuationIndex + 1)
: description.substring(0, maxLength);
}

description = truncateDescription(description);
</script>

<a id={name} href={link} data-sveltekit-reload>
<div
class="dark:bg-tertiaryDark'dark:shadow-white relative mb-5 inline-flex h-[80px] rounded-xl bg-tertiary p-2 shadow-gray-200 transition-all duration-100 shadow-lg hover:scale-110 hover:cursor-pointer"
>
<div class="pl-5 pr-5">
<div class="flex">
<h1 class="text-4xl font-bold text-black dark:text-white">{name}</h1>
</div>

<p class="text-black dark:text-white">{description}</p>
</div>
</div>
</a>
124 changes: 124 additions & 0 deletions src/routes/api/community/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import prisma from '$lib/prisma';

interface Opts {
request: Request;
}

interface Game {
name: string;
description: string;
embedURL: string;
}
// Post request
export async function POST({ request }: Opts): Promise<Response> {
// Edit the game given the new data sent as a json object which provides the id and all the new data
const data: Game = await request.json();

try {
const response = await fetch('https://vector.profanity.dev', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: data.description })
});

const json = await response.json();

// Log the response for debugging

// Check if the response indicates the presence of profanity
if (json.isProfanity) {
return new Response(JSON.stringify({ error: 'Bad Words 😢' }), {
status: 400
});
}

// Return nothing if there is no profanity
} catch (error) {
// Handle any errors that may occur during the request
console.error('Error during fetch:', error);
return new Response(JSON.stringify({ error: 'Error checking profanity' }), {
status: 500
});
}

try {
const response = await fetch('https://vector.profanity.dev', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: data.name })
});

const json = await response.json();

// Log the response for debugging

// Check if the response indicates the presence of profanity
if (json.isProfanity) {
return new Response(JSON.stringify({ error: 'Bad Words 😢' }), {
status: 400
});
}

// Return nothing if there is no profanity
} catch (error) {
// Handle any errors that may occur during the request
console.error('Error during fetch:', error);
return new Response(JSON.stringify({ error: 'Error checking profanity' }), {
status: 500
});
}
// Run inp
// Run input validation on all the data given
// Ensure that the name, id, description, image, developer, embedURL, emulatorCore, emulatorFile, emulatorType, errorMessage, platform, and tags are all strings

function isValidHttpUrl(url: string | null): boolean {
if (!url) return false; // Return false if the URL is null or empty

try {
// Attempt to create a URL object from the provided URL
const parsedUrl = new URL(url);

// Check if the protocol is either 'http:' or 'https:'
return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
} catch (e) {
// If an error occurs during parsing, the URL is invalid
return false;
}
}

// Assuming you are inside a function that handles the data:
if (data.embedURL != null && typeof data.embedURL !== 'string') {
return new Response(JSON.stringify({ error: 'Invalid input strings' }), {
status: 400
});
}

// Add the URL validation condition
if (data.embedURL && !isValidHttpUrl(data.embedURL)) {
return new Response(
JSON.stringify({ error: 'Invalid URL format. URL must be HTTP or HTTPS.' }),
{
status: 400
}
);
}

try {
await prisma.community.create({
data: {
name: data.name,
description: data.description,
embedURL: data.embedURL
}
});
} catch (error) {
// Return an error response
return new Response(JSON.stringify({ error: 'Database error' }), {
status: 500
});
}

return new Response(JSON.stringify({ success: true }), {
status: 200
});
}
34 changes: 34 additions & 0 deletions src/routes/community/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script>
import CommunityBox from '$lib/components/Box/CommunityBox.svelte';
</script>

<div class="flex gap-4">
<div>
<a href={'/community/add'}>
<div
class="dark:bg-tertiaryDark'dark:shadow-white relative mb-5 inline-flex h-[80px] w-[80px] rounded-xl p-2 shadow-gray-200 ring-tertiary transition-all duration-100 shadow-lg ring-4 hover:cursor-pointer"
>
<div>
<div class="flex">
<svg
xmlns="http://www.w3.org/2000/svg"
width="60px"
height="60px"
viewBox="0 0 24 24"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22ZM12.75 9C12.75 8.58579 12.4142 8.25 12 8.25C11.5858 8.25 11.25 8.58579 11.25 9L11.25 11.25H9C8.58579 11.25 8.25 11.5858 8.25 12C8.25 12.4142 8.58579 12.75 9 12.75H11.25V15C11.25 15.4142 11.5858 15.75 12 15.75C12.4142 15.75 12.75 15.4142 12.75 15L12.75 12.75H15C15.4142 12.75 15.75 12.4142 15.75 12C15.75 11.5858 15.4142 11.25 15 11.25H12.75V9Z"
fill="#1C274C"
/>
</svg>
</div>
</div>
</div>
</a>
</div>

<CommunityBox name={'hello'} description={'hello'} link={'hello'} />
</div>
83 changes: 83 additions & 0 deletions src/routes/community/add/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<script lang="ts">
import { PUBLIC_API_BASE_URL } from '$env/static/public';

// Fetch all the games

let selectedGame: any = {
name: '',
description: '',
embedURL: ''
};

function updateGame() {
// POST /api/admin/edit/game
fetch('/api/community', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(selectedGame)
})
.then((res) => {
if (res.status === 200) {
alert('Successfully added game!');
} else {
alert('Failed to add game! Remember to use https and to not use bad words!');
}
})
.catch((err) => {
alert('Failed to add game!');
});
}
</script>

<div class="rounded-3xl bg-tertiary p-8 text-black dark:bg-tertiaryDark dark:text-white">
<h1 class="text-left text-4xl font-bold">Community Game Adder</h1>
<div class="mt-2 text-xl">
<p>
You can add games as needed, but please be careful. If you are unsure of what you are doing,
please ask for help.
</p>
</div>

<div class="mt-10 grid grid-cols-1 gap-10">
<div class="col-start-1 row-start-1">
<h3 class="text-2xl font-bold">Game</h3>
<p class="text-sm text-gray-500">Input the vital information for the game you wish to add:</p>
<div class="mt-2 grid w-full gap-4 align-middle">
<div class="row-start-2 sm:row-start-1">
<div class="flex flex-col gap-2">
<div class="flex flex-col">
<label for="name" class="text-sm text-gray-500"> Name </label>
<input
id="name"
class="rounded-lg p-2 text-black"
type="text"
bind:value={selectedGame.name}
/>
</div>
<div class="flex flex-col">
<label for="description" class="text-sm text-gray-500"> Description </label>
<textarea
id="description"
class="rounded-lg p-2 text-black"
bind:value={selectedGame.description}
/>
</div>

<div class="flex flex-col">
<label for="embedURL" class="text-sm text-gray-500"> embedURL </label>
<input
id="embedURL"
class="rounded-lg p-2 text-black"
type="text"
bind:value={selectedGame.embedURL}
/>
</div>
</div>
</div>
</div>
<button class="btn-lg mt-2" on:click={() => updateGame()}>Add Game</button>
</div>
</div>
</div>