Skip to content
Closed
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
11 changes: 11 additions & 0 deletions backend/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "5000:5000"
volumes:
- ./backend:/app
- /app/node_modules
command: npm run start

16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Nest/docker-compose.yml
version: "3.9"

services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true
command: npm run dev
19 changes: 19 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

FROM --platform=linux/arm64 node:18-alpine


WORKDIR /app


COPY package*.json ./
RUN npm install


COPY . .


EXPOSE 3000


CMD ["npm", "run", "dev"]

72 changes: 72 additions & 0 deletions frontend/docker/frontend.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
FROM node:22-alpine AS base

# Install dependencies and build the project.
FROM base AS builder
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine
# to understand why libc6-compat might be needed.
ENV APK_CACHE_DIR="/app/.cache/apk" \
APK_SYMLINK_DIR="/etc/apk/cache" \
FORCE_COLOR=1 \
NPM_CACHE="/app/.npm" \
PNPM_HOME="/pnpm"

ENV PATH="$PNPM_HOME:$PATH"

RUN mkdir -p ${APK_CACHE_DIR} && \
ln -fns ${APK_CACHE_DIR} ${APK_SYMLINK_DIR}

RUN --mount=type=cache,target=${APK_CACHE_DIR} \
apk update && apk upgrade && apk add libc6-compat

WORKDIR /app

RUN --mount=type=cache,target=${NPM_CACHE} \
npm install --ignore-scripts -g pnpm --cache ${NPM_CACHE}

COPY --chmod=444 package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --ignore-scripts

COPY --chmod=444 .env .pnpmrc next.config.ts postcss.config.js tailwind.config.mjs tsconfig.json ./
COPY --chmod=555 public public
COPY --chmod=555 src src

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
ENV NEXT_TELEMETRY_DISABLED=1
RUN --mount=type=secret,id=RELEASE_VERSION \
--mount=type=secret,id=SENTRY_AUTH_TOKEN \
export NEXT_SENTRY_AUTH_TOKEN=$(cat /run/secrets/SENTRY_AUTH_TOKEN) && \
export RELEASE_VERSION=$(cat /run/secrets/RELEASE_VERSION) && \
pnpm run build

# Production image, copy all the files and run next.
FROM base AS runner
WORKDIR /app

ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 -G nodejs nextjs
# Copying files with root as owner, so that executing user cannot change the container.
COPY --from=builder --chown=root:root --chmod=555 /app/public public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
COPY --from=builder --chown=root:root --chmod=555 /app/.next/standalone .

# Create cache directory and assign ownership to nextjs user with write permission, so that cache can be stored.
RUN mkdir -p /app/.next/cache && chown -R nextjs:nodejs /app/.next/cache && chmod -R 755 /app/.next/cache && rm .env
COPY --from=builder --chown=root:root --chmod=555 /app/.next/static .next/static

USER nextjs

EXPOSE 3000

ENV HOSTNAME="0.0.0.0"
ENV PORT=3000

# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
CMD ["node", "server.js"]
114 changes: 78 additions & 36 deletions frontend/src/components/MultiSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,42 +120,84 @@ const MultiSearchBar: React.FC<MultiSearchBarProps> = ({
)

useEffect(() => {
const closeSuggestions = () => {
setShowSuggestions(false)
inputRef.current?.blur()
}

const selectHighlightedSuggestion = () => {
if (highlightedIndex === null) return
const { index, subIndex } = highlightedIndex
const suggestionGroup = suggestions[index]
if (!suggestionGroup) return
const suggestion = suggestionGroup.hits[subIndex]
if (!suggestion) return

handleSuggestionClick(
suggestion as Chapter | Organization | Project | User | Event,
suggestionGroup.indexName
)
}

const moveHighlightDown = () => {
if (suggestions.length === 0) return
if (highlightedIndex === null) {
setHighlightedIndex({ index: 0, subIndex: 0 })
return
}

const { index, subIndex } = highlightedIndex
const currentGroup = suggestions[index]
const hits = currentGroup?.hits ?? []
if (hits.length === 0) return

if (subIndex < hits.length - 1) {
setHighlightedIndex({ index, subIndex: subIndex + 1 })
return
}

if (index >= suggestions.length - 1) return
setHighlightedIndex({ index: index + 1, subIndex: 0 })
}

const moveHighlightUp = () => {
if (highlightedIndex === null) return

const { index, subIndex } = highlightedIndex
if (subIndex > 0) {
setHighlightedIndex({ index, subIndex: subIndex - 1 })
return
}

if (index <= 0) return
const previousGroup = suggestions[index - 1]
const hits = previousGroup?.hits ?? []
if (hits.length === 0) return

setHighlightedIndex({
index: index - 1,
subIndex: hits.length - 1,
})
}

const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setShowSuggestions(false)
inputRef.current?.blur()
} else if (event.key === 'Enter' && highlightedIndex !== null) {
const { index, subIndex } = highlightedIndex
const suggestion = suggestions[index].hits[subIndex]
handleSuggestionClick(
suggestion as Chapter | Organization | Project | User | Event,
suggestions[index].indexName
)
} else if (event.key === 'ArrowDown') {
event.preventDefault()
if (highlightedIndex === null) {
setHighlightedIndex({ index: 0, subIndex: 0 })
} else {
const { index, subIndex } = highlightedIndex
if (subIndex < suggestions[index].hits.length - 1) {
setHighlightedIndex({ index, subIndex: subIndex + 1 })
} else if (index < suggestions.length - 1) {
setHighlightedIndex({ index: index + 1, subIndex: 0 })
}
}
} else if (event.key === 'ArrowUp') {
event.preventDefault()
if (highlightedIndex !== null) {
const { index, subIndex } = highlightedIndex
if (subIndex > 0) {
setHighlightedIndex({ index, subIndex: subIndex - 1 })
} else if (index > 0) {
setHighlightedIndex({
index: index - 1,
subIndex: suggestions[index - 1].hits.length - 1,
})
}
}
switch (event.key) {
case 'Escape':
closeSuggestions()
return
case 'Enter':
selectHighlightedSuggestion()
return
case 'ArrowDown':
event.preventDefault()
moveHighlightDown()
return
case 'ArrowUp':
event.preventDefault()
moveHighlightUp()
return
default:
return
}
}

Expand All @@ -164,7 +206,7 @@ const MultiSearchBar: React.FC<MultiSearchBarProps> = ({
return () => {
document.removeEventListener('keydown', handleKeyDown)
}
}, [searchQuery, suggestions, highlightedIndex, handleSuggestionClick])
}, [suggestions, highlightedIndex, handleSuggestionClick])

useEffect(() => {
inputRef.current?.focus()
Expand Down
1 change: 1 addition & 0 deletions welcome-to-docker
Submodule welcome-to-docker added at c8cd4b