Skip to content

Commit

Permalink
TEST-0004 Add vike (#123)
Browse files Browse the repository at this point in the history
<!--- Provide a general summary of your changes in the Title above -->

## Description 💬
<!--- Describe your changes in detail -->

## Motivation and Context 🥅
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## How has this been tested? 🧪
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how
-->
<!--- your change affects other areas of the code, etc. -->
- [ ] Local build ⚒️
- [ ] Local tests 🧪
- [ ] (optional) Local run and endpoint tested in swagger 🚀

## Screenshots (if appropriate) 💻

## Types of changes 🌊
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)

## Checklist ☑️

<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [ ] The pull request title starts with the jira case number (when
applicable), e.g. "TEST-1234 Add some feature"
- [ ] The person responsible for following up on requested review
changes has been assigned to the pull request
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.

## Highly optional checks, only use these if you have a reason to do so
✔️

- [ ] This PR changes the database so I have added the *create-diagram*
label to assist reviewers with a db diagram
- [ ] This PR changes platform or backend and I need others to be able
to test against these changes before merging to dev, so I have added the
*deploy-azure* label to deploy before merging the PR

## Checklist for the approver ✅

- [ ] I've checked the files view for spelling issues, code quality
warnings and similar
- [ ] I've waited until all checks have passed (green check/without
error)
- [ ] I've checked that only the intended files are changed
  • Loading branch information
hwinther authored Jun 29, 2024
2 parents b523087 + 238b5c3 commit 6181708
Show file tree
Hide file tree
Showing 30 changed files with 644 additions and 67 deletions.
9 changes: 6 additions & 3 deletions src/backend/Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
.PHONY: run
run:
.PHONY: dirs
dirs:
mkdir -p docker-volumes/log && chmod 777 docker-volumes/log
mkdir -p docker-volumes/rabbitmq/data && chmod 777 docker-volumes/rabbitmq/data
mkdir -p docker-volumes/rabbitmq/log && chmod 777 docker-volumes/rabbitmq/log
mkdir -p docker-volumes/mssql/data && chmod 777 docker-volumes/mssql/data
mkdir -p docker-volumes/mssql/log && chmod 777 docker-volumes/mssql/log
mkdir -p docker-volumes/mssql/secrets && chmod 777 docker-volumes/mssql/secrets

.PHONY: run
run: dirs
docker compose up -d --build

.PHONY: clean
clean:
docker compose down --remove-orphans
rm -rf log rabbitmq
rm -rf docker-volumes

.PHONY: wait
wait:
Expand Down
32 changes: 32 additions & 0 deletions src/backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Backend local development setup

## Start local SQL server

```bash
cd src/backend

# Create docker-volumes directory structure if it doesn't already exist
make dirs

# Run SQL server
podman run --rm -e ACCEPT_EULA=Y -e SA_PASSWORD=DevelopmentOnlyPassword1 -p 1433:1433 -v .\\docker-volumes\\mssql\\data:/var/opt/mssql/data -v .\\docker-volumes\\mssql\\log:/var/opt/mssql/log -v .\\docker-volumes\\mssql\\secrets:/var/opt/mssql/secrets --name mssql-dev -it mcr.microsoft.com/mssql/server:2022-latest
```

## Start local backend instance

```bash
# Windows
set ASPNETCORE_ENVIRONMENT=Development
set DB_CONNECTION="Server=127.0.0.1,1433;Initial Catalog=api;User=sa;Password=DevelopmentOnlyPassword1;TrustServerCertificate=True;"

# Linux/mac
ASPNETCORE_ENVIRONMENT=Development
DB_CONNECTION="Server=127.0.0.1,1433;Initial Catalog=api;User=sa;Password=DevelopmentOnlyPassword1;TrustServerCertificate=True;"

# TODO: broken due to docker compose solution file
dotnet run WebApi

# Current method:
cd src/backend/WebApi
dotnet run -lp "https swagger"
```
5 changes: 4 additions & 1 deletion src/frontend/.dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
**/node_modules/
**/node_modules/
**/dist/
**/obj/
**/.vscode/
7 changes: 5 additions & 2 deletions src/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ RUN yarn build

FROM nginx:alpine-otel AS final
RUN apk update && apk upgrade
COPY --from=build /app/dist /usr/share/nginx/html/
EXPOSE 80
COPY --from=build /app/dist/client /usr/share/nginx/html/
COPY ssl/localhost.crt /etc/ssl/certs/localhost.crt
COPY ssl/localhost.key /etc/ssl/private/localhost.key
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 8080 8443
CMD ["nginx", "-g", "daemon off;"]
15 changes: 15 additions & 0 deletions src/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,18 @@ export default {
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list

## Local container build and launch

```bash
# Make localhost certificate if it's missing
# cd ssl && make && cd ..

# Build container
podman build -f Dockerfile . -t frontend-test

# Run container
docker run --rm -p 5173:8443 -it frontend-test

# Navigate to https://localhost:5173/ in browser
```
8 changes: 6 additions & 2 deletions src/frontend/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
"words": [
"githubusercontent",
"mkdirp",
"precached",
"nocheck",
"precached",
"streetsidesoftware",
"tsmerge",
"vike",
"visualstudio",
"vsmarketplacebadge"
],
"ignorePaths": ["node_modules/**", "*.lock"]
"ignorePaths": [
"node_modules/**",
"*.lock"
]
}
12 changes: 12 additions & 0 deletions src/frontend/nginx/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server {
listen 8080;
listen 8443 ssl;
server_name localhost;
ssl_certificate /etc/ssl/certs/localhost.crt;
ssl_certificate_key /etc/ssl/private/localhost.key;

location / {
root /usr/share/nginx/html;
index index.html;
}
}
6 changes: 5 additions & 1 deletion src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
"@tanstack/react-query-devtools": "^5.35.1",
"axios": "^1.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-streaming": "^0.3.33",
"vike": "^0.4.177",
"vike-react": "^0.4.15",
"vike-react-query": "^0.1.0"
},
"devDependencies": {
"@cspell/eslint-plugin": "^8.9.1",
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/src/api/endpoints/blogging/blogging.msw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ import type { BlogDto, PostDto } from '../../models'
export const getGetBlogsResponseMock = (): BlogDto[] =>
Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({
blogId: faker.helpers.arrayElement([faker.number.int({ min: undefined, max: undefined }), undefined]),
title: faker.helpers.arrayElement([faker.word.sample(), null]),
url: faker.helpers.arrayElement([faker.word.sample(), null]),
}))

export const getPostBlogResponseMock = (overrideResponse: Partial<BlogDto> = {}): BlogDto => ({
blogId: faker.helpers.arrayElement([faker.number.int({ min: undefined, max: undefined }), undefined]),
title: faker.helpers.arrayElement([faker.word.sample(), null]),
url: faker.helpers.arrayElement([faker.word.sample(), null]),
...overrideResponse,
})

export const getGetBlogResponseMock = (overrideResponse: Partial<BlogDto> = {}): BlogDto => ({
blogId: faker.helpers.arrayElement([faker.number.int({ min: undefined, max: undefined }), undefined]),
title: faker.helpers.arrayElement([faker.word.sample(), null]),
url: faker.helpers.arrayElement([faker.word.sample(), null]),
...overrideResponse,
})
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/src/api/models/blogDto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
export interface BlogDto {
/** Gets or sets the unique identifier for the blog. */
blogId?: number
/**
* Gets or sets the title of the blog.
* @nullable
*/
title: string | null
/**
* Gets or sets the URL of the blog.
* @nullable
Expand Down
36 changes: 0 additions & 36 deletions src/frontend/src/main.tsx

This file was deleted.

3 changes: 3 additions & 0 deletions src/frontend/src/pages/+config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
// Add settings here later
}
65 changes: 65 additions & 0 deletions src/frontend/src/pages/_error/+Page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react'
import { type PageContext } from 'vike/types'
import { usePageContext } from 'vike-react/usePageContext'

/**
* Page component.
* @returns {React.JSX.Element} The rendered component.
*/
function Page(): React.JSX.Element {
const pageContext = usePageContext() as ExtendedPageContext & PageContext

let msg: string // Message shown to the user
const { abortReason, abortStatusCode } = pageContext
if (typeof abortReason === 'object' && abortReason?.notAdmin) {
// Handle `throw render(403, { notAdmin: true })`
msg = "You cannot access this page because you aren't an administrator."
} else if (typeof abortReason === 'string') {
// Handle `throw render(abortStatusCode, `You cannot access ${someCustomMessage}`)`
msg = abortReason
} else if (abortStatusCode === 403) {
// Handle `throw render(403)`
msg = "You cannot access this page because you don't have enough privileges."
} else if (abortStatusCode === 401) {
// Handle `throw render(401)`
msg = "You cannot access this page because you aren't logged in. Please log in."
} else {
// Fallback error message
msg =
pageContext.is404 ?? false
? "This page doesn't exist."
: 'Something went wrong. Sincere apologies. Try again (later).'
}

return (
<Center>
<p style={{ fontSize: '1.3em' }}>{msg}</p>
</Center>
)
}

/**
* Center component.
* @param {Readonly<{ children: React.ReactNode }>} props - The component props.
* @returns {React.JSX.Element} The rendered component.
*/
function Center({ children }: Readonly<{ children: React.ReactNode }>): React.JSX.Element {
return (
<div
style={{
alignItems: 'center',
display: 'flex',
height: 'calc(100vh - 100px)',
justifyContent: 'center',
}}
>
{children}
</div>
)
}

export interface ExtendedPageContext {
abortReason?: { notAdmin: true } | string
}

export default Page
14 changes: 14 additions & 0 deletions src/frontend/src/pages/about/+Page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'

/**
* Renders the About page.
* @returns {React.JSX.Element} The rendered About page.
*/
export function Page(): React.JSX.Element {
return (
<>
<h1>About</h1>
<p>This app showcases a migration from Vite to Vike.</p>
</>
)
}
30 changes: 30 additions & 0 deletions src/frontend/src/pages/blogs/+Page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react'

import { useGetBlogs } from '~/api/endpoints/blogging/blogging'
import { Link } from '~/renderer/Link'

const BlogListView: React.FC = () => {
const { data: blogs, error, isLoading } = useGetBlogs()

if (isLoading) return <div>Loading...</div>
if (error != null) return <div>Error loading blogs</div>

return (
<div>
<h2>Blogs</h2>
<ul>
{blogs?.map((blog) => (
<li key={blog.blogId}>
<p>
<Link className="is-active" href={`/blogs/${blog.blogId}`}>
{blog.title} - ({blog.url})
</Link>
</p>
</li>
))}
</ul>
</div>
)
}

export default BlogListView
37 changes: 37 additions & 0 deletions src/frontend/src/pages/blogs/@id/+Page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import { usePageContext } from 'vike-react/usePageContext'

import { useGetBlog, useGetPosts } from '~/api/endpoints/blogging/blogging'
import { Link } from '~/renderer/Link'

const BlogListView: React.FC = () => {
const pageContext = usePageContext()
const id = pageContext?.routeParams?.id ?? window.location.pathname.split('/').at(-1)
const { data: blog, error: blogError, isLoading: blogIsLoading } = useGetBlog(parseInt(id ?? '0'))
const { data: posts, error: postsError, isLoading: postsIsLoading } = useGetPosts(parseInt(id ?? '0'))

if (blogIsLoading || postsIsLoading) return <div>Loading...</div>
if (blogError != null || blog === null || postsError != null || posts === null) return <div>Error loading blog</div>

return (
<div>
<h2>
Blog: {blog?.title} - {blog?.url}
</h2>
<h3>Posts</h3>
<ul>
{posts?.map((post) => (
<li key={post.postId}>
<p>
<Link className="is-active" href={`/blog/${blog?.blogId}/post/${post?.postId}`}>
{post.title} - ({post.content})
</Link>
</p>
</li>
))}
</ul>
</div>
)
}

export default BlogListView
Loading

0 comments on commit 6181708

Please sign in to comment.