Skip to content

Commit

Permalink
Merge branch 'feature/ts/redis-query-caching'
Browse files Browse the repository at this point in the history
  • Loading branch information
t-shah02 committed Feb 26, 2024
2 parents 71ca27d + fd10784 commit 006b271
Show file tree
Hide file tree
Showing 43 changed files with 810 additions and 186 deletions.
50 changes: 0 additions & 50 deletions .github/workflows/deploy-preview.yml

This file was deleted.

4 changes: 3 additions & 1 deletion .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ env:
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
DATABASE_REDIS_URL: ${{ secrets.PROD_DATABASE_REDIS_URL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
Expand All @@ -26,7 +27,7 @@ jobs:
run: npm install --global vercel@latest

- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
run: vercel pull --yes --environment=production --token=$VERCEL_TOKEN

- name: Build Project Artifacts
run: vercel build --prod --token=$VERCEL_TOKEN
Expand All @@ -38,6 +39,7 @@ jobs:
run: |
vercel deploy --token=$VERCEL_TOKEN \
--env DATABASE_URL=$DATABASE_URL \
--env DATABASE_REDIS_URL=$DATABASE_REDIS_URL \
--env AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \
--env AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \
--env AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION \
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ brew --version
# Local development setup
Firstly as with all projects, clone this repository on your machine:
```bash
git clone https://github.com/t-shah02/dexbooru-rewrite.git
git clone https://github.com/Dexbooru/dexbooru-web.git
cd dexbooru-rewrite
```

Expand All @@ -64,8 +64,11 @@ DB_PASSWORD="root"
DB_SCHEMA="public"
DB_HOST="host.docker.internal"
DATABASE_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?schema=${DB_SCHEMA}"
DB_REDIS_HOST="localhost"
DB_REDIS_PORT="6379"
DB_REDIS_USER="default"
DB_REDIS_PASSWORD="root"
DATABASE_REDIS_URL="redis://${DB_REDIS_USER}:${DB_REDIS_PASSWORD}@${DB_REDIS_HOST}:${DB_REDIS_PORT}"
# AWS secrets, base urls and S3 bucket names
AWS_ACCESS_KEY_ID=
Expand Down
3 changes: 2 additions & 1 deletion docker/postgres/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ services:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
ports:
- "${DB_PORT}:5432"
- "${DB_PORT}:${DB_PORT}"
volumes:
- ./data:/var/lib/postgresql/data
container_name: dexbooru-postgres

4 changes: 3 additions & 1 deletion docker/redis/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ services:
redis:
image: redis:latest
ports:
- "${DB_REDIS_PORT}:6379"
- "${DB_REDIS_PORT}:${DB_REDIS_PORT}"
environment:
REDIS_HOST: ${DB_REDIS_HOST}
REDIS_USER: ${DB_REDIS_USER}
REDIS_PASSWORD: ${DB_REDIS_PASSWORD}
REDIS_DISABLE_COMMANDS: "FLUSHDB,FLUSHALL"
volumes:
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"postcss-load-config": "^4.0.1",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"prisma": "^5.9.1",
"prisma": "^5.10.2",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"tailwindcss": "^3.3.2",
Expand All @@ -56,10 +56,12 @@
"type": "module",
"dependencies": {
"@aws-sdk/client-s3": "^3.421.0",
"@prisma/client": "^5.9.1",
"@prisma/client": "^5.10.2",
"@vercel/analytics": "^1.2.2",
"@zerodevx/svelte-toast": "^0.9.5",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"redis": "^4.6.13",
"sharp": "^0.32.6"
},
"lint-staged": {
Expand Down
19 changes: 16 additions & 3 deletions scripts/setup-db.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
#!/bin/bash

ENV_LOCAL_FILE_PATH="../.env.local"
ENV_LOCAL_FILE_PATH_DOCKER="../../.env.local"

echo "Reading .env.local variables into script environment"
export $(egrep -v '^#' $ENV_LOCAL_FILE_PATH | xargs)

cd ../docker/postgres
echo "Composing dexbooru database container from Docker Postgres image"
docker-compose --env-file "../../.env.local" up -d
docker-compose --env-file $ENV_LOCAL_FILE_PATH_DOCKER up -d

cd ../redis
echo "Composing dexbooru redis container from Docker Redis image"
docker-compose --env-file "../../.env.local" up -d
docker-compose --env-file $ENV_LOCAL_FILE_PATH_DOCKER up -d

echo "Configuring permissions for the user called $DB_USER on the database called $DB_NAME"
docker exec -it dexbooru-postgres psql -U ${DB_USER} -d ${DB_NAME} -c "ALTER USER ${DB_USER} WITH SUPERUSER;"
docker exec -it dexbooru-postgres psql -U ${DB_USER} -d ${DB_NAME} -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};"
docker exec -it dexbooru-postgres psql -U ${DB_USER} -d ${DB_NAME} -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ${DB_SCHEMA} TO ${DB_USER};"

cd ../../
echo "Migrating schemas and Seeding the database with mock data"
yarn dbmigrate:dev
yarn dbreset:dev



7 changes: 0 additions & 7 deletions src/index.test.ts

This file was deleted.

Binary file modified src/lib/client/assets/404graphic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/lib/client/components/images/ImageCarousel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
let index: number = 0;
const imagesData: HTMLImgAttributes[] = imageUrls.map((imageUrl, index) => {
return {
loading: 'lazy',
src: imageUrl,
alt: imagesAlt
? imagesAlt
Expand Down
16 changes: 11 additions & 5 deletions src/lib/client/components/layout/Navbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@
import { page } from '$app/stores';
import { authenticatedUserStore } from '$lib/client/stores/users';
import { Button, DarkMode, NavBrand, NavHamburger, NavLi, NavUl, Navbar } from 'flowbite-svelte';
import GlobalSearchbar from '../search/GlobalSearchbar.svelte';
import ProfileDropdown from './ProfileDropdown.svelte';
const currentPath = $page.url.pathname;
</script>

<Navbar class="sticky top-0 z-50">
<NavBrand href="/">
<img src="/favicon.png" class="mr-3 h-6 sm:h-9 rounded-md" alt="Dexbooru Logo" />
<span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white">Dexbooru</span
>
</NavBrand>
<div class="flex space-x-4">
<NavBrand href="/">
<img src="/favicon.png" class="mr-3 h-6 sm:h-9 rounded-md" alt="Dexbooru Logo" />
<span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white"
>Dexbooru</span
>
</NavBrand>
<GlobalSearchbar />
</div>

<div class="flex md:order-2 space-x-2">
{#if $authenticatedUserStore}
<ProfileDropdown />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@
<div id="post-container-body" class="space-y-4 mb-5">
<div id="post-container-title" class="flex justify-between">
<p class="text-4xl dark:text-white">{postContainerTitle}</p>
<Searchbar queryHandler={onPostSearch} placeholder="Search by keyword(s)" />
<Searchbar queryInputHandler={onPostSearch} placeholder="Search by keyword(s)" />
</div>

<PostGrid />
<PostPaginator />
<PostPaginator noPostsLeft={$originalPostsPageStore.length === 0} />
</div>
</main>

Expand Down
15 changes: 15 additions & 0 deletions src/lib/client/components/reusable/HighlightedText.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import { getQueryParts } from '$lib/client/helpers/search';
import { normalizeQuery } from '$lib/shared/helpers/search';
export let query: string;
export let fullText: string;
const fullTextParts = getQueryParts(normalizeQuery(fullText), normalizeQuery(query));
</script>

<p>
{#each fullTextParts as { text, type }}
<span class={type === 'highlight' ? 'bg-yellow-400' : ''}>{text}</span>
{/each}
</p>
40 changes: 29 additions & 11 deletions src/lib/client/components/reusable/Searchbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,42 @@
import { Input } from 'flowbite-svelte';
import { SearchOutline } from 'flowbite-svelte-icons';
export let inputElementId: string | null = null;
export let width: string = '300px';
export let isGlobal: boolean = false;
export let placeholder: string;
export let queryHandler: (query: string) => void;
export let queryInputHandler: (query: string) => void;
export let queryChangeHandler: ((query: string) => void) | null = null;
const onInput = (event: Event) => {
const optionalProps: Record<string, string> = {};
if (inputElementId) {
optionalProps.id = inputElementId;
}
const handleOnInput = (event: Event) => {
const inputTarget = event.target as HTMLInputElement;
queryHandler(inputTarget.value);
if (inputTarget.value.length > 0) {
queryInputHandler(inputTarget.value);
}
};
const handleOnChange = (event: Event) => {
const inputTarget = event.target as HTMLInputElement;
if (queryChangeHandler) {
queryChangeHandler(inputTarget.value);
}
};
</script>

<div class="hidden relative md:block mr-4 searchbar-container">
<div class="hidden relative md:block {!isGlobal && 'mr-4'}" style="width: {width}">
<div class="flex absolute inset-y-0 start-0 items-center ps-3 pointer-events-none">
<SearchOutline class="w-4 h-4" />
</div>
<Input on:input={onInput} class="ps-10" {placeholder} />
<Input
{...optionalProps}
on:input={handleOnInput}
on:input={handleOnChange}
class="ps-10"
{placeholder}
/>
</div>

<style>
.searchbar-container {
width: 300px;
}
</style>
91 changes: 91 additions & 0 deletions src/lib/client/components/search/GlobalSearchModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<script lang="ts">
import { getGlobalSearchResults } from '$lib/client/api/search';
import {
GLOBAL_SEARCH_INPUT_ELEMENT_ID,
SEARCH_DEBOUNCE_TIMEOUT_MS
} from '$lib/client/constants/search';
import { FAILURE_TOAST_OPTIONS } from '$lib/client/constants/toasts';
import { debounce, memoize } from '$lib/client/helpers/util';
import { searchModalActiveStore } from '$lib/client/stores/layout';
import { queryStore } from '$lib/client/stores/search';
import type { IAppSearchResult } from '$lib/shared/types/search';
import { toast } from '@zerodevx/svelte-toast';
import { Modal, Spinner } from 'flowbite-svelte';
import { onDestroy } from 'svelte';
import Searchbar from '../reusable/Searchbar.svelte';
import SearchResultsContainer from './SearchResultsContainer.svelte';
let currentSearchResults: IAppSearchResult | null = null;
let searchResultsLoading = false;
const searchModalUnsubsribe = searchModalActiveStore.subscribe((active) => {
if (active) {
const globalSearchInput = document.querySelector(
`#${GLOBAL_SEARCH_INPUT_ELEMENT_ID}`
) as HTMLInputElement | null;
if (globalSearchInput) {
globalSearchInput.focus();
}
} else {
currentSearchResults = null;
}
});
const fetchQueryResults = memoize(async (query: string) => {
const response = await getGlobalSearchResults(query);
if (response.ok) {
return (await response.json()) as IAppSearchResult;
} else {
toast.push(
'An unexpected error occured while retrieving the search results',
FAILURE_TOAST_OPTIONS
);
return null;
}
}, true);
const clearResultsOnEmptyQuery = (query: string) => {
if (query.length === 0) {
currentSearchResults = null;
}
};
const debouncedFetchQueryResults = debounce(async (query: string) => {
searchResultsLoading = true;
queryStore.set(query);
currentSearchResults = query ? await fetchQueryResults(query as never) : null;
searchResultsLoading = false;
}, SEARCH_DEBOUNCE_TIMEOUT_MS) as (query: string) => void;
onDestroy(() => {
searchModalUnsubsribe();
});
</script>

<Modal
title="Find tags, artists, users and posts"
open={$searchModalActiveStore}
outsideclose
class="w-screen"
placement="top-center"
on:close={() => searchModalActiveStore.set(false)}
>
<div class="flex relative">
<Searchbar
inputElementId={GLOBAL_SEARCH_INPUT_ELEMENT_ID}
isGlobal
width="100%"
placeholder="Enter your search query"
queryInputHandler={debouncedFetchQueryResults}
queryChangeHandler={clearResultsOnEmptyQuery}
/>
{#if searchResultsLoading}
<Spinner color="pink" class="absolute top-2 right-2" size="7" />
{/if}
</div>

{#if currentSearchResults}
<SearchResultsContainer results={currentSearchResults} />
{/if}
</Modal>
Loading

0 comments on commit 006b271

Please sign in to comment.