mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-16 11:51:49 +08:00
Compare commits
2 Commits
fix/title
...
fix/layout
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52018e7030 | ||
|
|
95d70c521a |
72
.github/workflows/rsync-ssr.yml
vendored
72
.github/workflows/rsync-ssr.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Deploy to EC2
|
||||
on:
|
||||
workflow_dispatch: # allow manual run
|
||||
push:
|
||||
branches:
|
||||
- feat/ssr
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3.0.0
|
||||
with:
|
||||
version: 8.15.6
|
||||
|
||||
# --------------------
|
||||
# Setup configuration
|
||||
# --------------------
|
||||
- name: Prepare configuration files
|
||||
run: |
|
||||
git clone https://${{ secrets.GH_PAT }}@github.com/roadmapsh/infra-config.git configuration --depth 1
|
||||
- name: Copy configuration files
|
||||
run: |
|
||||
cp configuration/dist/github/developer-roadmap.env .env
|
||||
|
||||
# --------------------
|
||||
# Prepare the build
|
||||
# --------------------
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
- name: Generate build
|
||||
run: |
|
||||
git clone https://${{ secrets.GH_PAT }}@github.com/roadmapsh/web-draw.git .temp/web-draw --depth 1
|
||||
npm run generate-renderer
|
||||
npm run build
|
||||
|
||||
# --------------------
|
||||
# Deploy to EC2
|
||||
# --------------------
|
||||
- uses: webfactory/ssh-agent@v0.7.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.EC2_PRIVATE_KEY }}
|
||||
- name: Deploy app to EC2
|
||||
run: |
|
||||
rsync -avz --omit-dir-times --exclude ".git" --exclude "configuration" -e "ssh -o StrictHostKeyChecking=no" -p ./ ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/var/www/v2.roadmap.sh/
|
||||
- name: Restart PM2
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.EC2_HOST }}
|
||||
username: ${{ secrets.EC2_USERNAME }}
|
||||
key: ${{ secrets.EC2_PRIVATE_KEY }}
|
||||
script: |
|
||||
cd /var/www/v2.roadmap.sh
|
||||
sudo pm2 restart web-roadmap
|
||||
|
||||
# --------------------
|
||||
# Clear Cloudfront Caching
|
||||
# --------------------
|
||||
- name: Clear Cloudfront Caching
|
||||
run: |
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GH_PAT }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/roadmapsh/infra-ansible/actions/workflows/playbook.yml/dispatches \
|
||||
-d '{ "ref":"master", "inputs": { "playbook": "roadmap_web.yml", "tags": "cloudfront", "is_verbose": false } }'
|
||||
@@ -246,7 +246,7 @@ export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
|
||||
key={result?._id}
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex w-full items-start rounded p-2 text-left text-sm',
|
||||
'flex w-full items-center rounded p-2 text-sm',
|
||||
counter === activeCounter ? 'bg-gray-100' : '',
|
||||
)}
|
||||
onMouseOver={() => setActiveCounter(counter)}
|
||||
@@ -264,7 +264,7 @@ export function AITermSuggestionInput(props: AITermSuggestionInputProps) {
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
'mr-2 whitespace-nowrap rounded-full p-1 px-1.5 text-xs leading-none',
|
||||
'mr-2 rounded-full p-1 px-1.5 text-xs leading-none',
|
||||
result.isOfficial
|
||||
? 'bg-green-500 text-green-50'
|
||||
: 'bg-blue-400 text-blue-50',
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
import { getGuideTableOfContent, type GuideFileType } from '../../lib/guide';
|
||||
import MarkdownFile from '../MarkdownFile.astro';
|
||||
import { TableOfContent } from '../TableOfContent/TableOfContent';
|
||||
|
||||
interface Props {
|
||||
guide: GuideFileType;
|
||||
}
|
||||
|
||||
const { guide } = Astro.props;
|
||||
|
||||
const allHeadings = guide.getHeadings();
|
||||
const tableOfContent = getGuideTableOfContent(allHeadings);
|
||||
|
||||
const showTableOfContent = tableOfContent.length > 0;
|
||||
const { frontmatter: guideFrontmatter, author } = guide;
|
||||
---
|
||||
|
||||
<article class='lg:grid lg:max-w-full lg:grid-cols-[1fr_minmax(0,700px)_1fr]'>
|
||||
{
|
||||
showTableOfContent && (
|
||||
<div class='bg-gradient-to-r from-gray-50 py-0 lg:col-start-3 lg:col-end-4 lg:row-start-1'>
|
||||
<TableOfContent toc={tableOfContent} client:load />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div
|
||||
class:list={['col-start-2 col-end-3 row-start-1 mx-auto max-w-[700px] py-5 sm:py-10', {
|
||||
'lg:border-r': showTableOfContent
|
||||
}]}
|
||||
>
|
||||
<MarkdownFile>
|
||||
<h1 class='text-balance text-4xl mb-3 font-bold'>{guideFrontmatter.title}</h1>
|
||||
<p
|
||||
class='flex items-center justify-start text-sm text-gray-400 my-0'
|
||||
>
|
||||
<a
|
||||
href={`/authors/${author.id}`}
|
||||
class='inline-flex items-center font-medium hover:text-gray-600 hover:underline underline-offset-2'
|
||||
>
|
||||
<img
|
||||
alt={author.frontmatter.name}
|
||||
src={author.frontmatter.imageUrl}
|
||||
class='mb-0 mr-2 inline h-5 w-5 rounded-full'
|
||||
/>
|
||||
{author.frontmatter.name}
|
||||
</a>
|
||||
<span class='mx-2 hidden sm:inline'>·</span>
|
||||
<a
|
||||
class='hover:text-gray-600 underline-offset-2 hidden sm:inline'
|
||||
href={`https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data/guides/${guide.id}.md`}
|
||||
target='_blank'
|
||||
>
|
||||
Improve this Guide
|
||||
</a>
|
||||
</p>
|
||||
<guide.Content />
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</article>
|
||||
@@ -7,8 +7,6 @@ export interface Props {
|
||||
|
||||
const { guide } = Astro.props;
|
||||
const { frontmatter, author } = guide;
|
||||
|
||||
return undefined;
|
||||
---
|
||||
|
||||
<div class='border-b bg-white py-5 sm:py-12'>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div
|
||||
class='container prose-h2:text-balance prose-h3:text-balance prose-h4:text-balance prose-h5:text-balance prose prose-xl prose-h2:mb-3 prose-h2:mt-10 prose-h2:scroll-mt-5 prose-h2:text-3xl prose-h3:mt-2 prose-h3:scroll-mt-5 prose-h5:font-medium prose-blockquote:font-normal prose-code:bg-transparent prose-img:mt-1 prose-h2:sm:scroll-mt-10 prose-h3:sm:scroll-mt-10'
|
||||
class='prose-xl prose-blockquote:font-normal prose container prose-code:bg-transparent prose-h2:text-3xl prose-h2:mt-10 prose-h2:mb-3 prose-h5:font-medium prose-h3:mt-2 prose-img:mt-1'
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -17,10 +17,12 @@ import { AccountDropdown } from './AccountDropdown';
|
||||
</a>
|
||||
|
||||
<a
|
||||
href='/ai'
|
||||
target='_blank'
|
||||
rel='noreferrer nofollow'
|
||||
href='https://boards.greenhouse.io/insightmediagroupllc/jobs/4002116008'
|
||||
class='group inline sm:hidden relative !mr-2 text-blue-300 hover:text-white'
|
||||
>
|
||||
AI Roadmaps
|
||||
We're Hiring
|
||||
|
||||
<span class='absolute -right-[11px] top-0'>
|
||||
<span class='relative flex h-2 w-2'>
|
||||
@@ -41,10 +43,12 @@ import { AccountDropdown } from './AccountDropdown';
|
||||
</a>
|
||||
<a href='/teams' class='text-gray-400 hover:text-white'> Teams</a>
|
||||
<a
|
||||
href='/ai'
|
||||
target='_blank'
|
||||
rel='noreferrer nofollow'
|
||||
href='https://boards.greenhouse.io/insightmediagroupllc/jobs/4002116008'
|
||||
class='group relative !mr-2 text-blue-300 hover:text-white'
|
||||
>
|
||||
AI Roadmaps
|
||||
We're Hiring
|
||||
|
||||
<span class='absolute -right-[11px] top-0'>
|
||||
<span class='relative flex h-2 w-2'>
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import { useState, type CSSProperties } from 'react';
|
||||
import type { HeadingGroupType } from '../../lib/guide';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { cn } from '../../lib/classname';
|
||||
|
||||
type TableOfContentProps = {
|
||||
toc: HeadingGroupType[];
|
||||
};
|
||||
|
||||
export function TableOfContent(props: TableOfContentProps) {
|
||||
const { toc } = props;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
if (toc.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const totalRows = toc.flatMap((heading) => {
|
||||
return [heading, ...heading.children];
|
||||
}).length;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'relative min-w-[250px] px-5 pt-0 max-lg:min-w-full max-lg:max-w-full max-lg:border-none max-lg:px-0 lg:pt-10',
|
||||
{
|
||||
'top-0 lg:!sticky': totalRows <= 20,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<h4 className="text-lg font-medium max-lg:hidden">In this article</h4>
|
||||
<button
|
||||
className="flex w-full items-center justify-between gap-2 bg-gray-300 px-3 py-2 text-sm font-medium lg:hidden"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
Table of Contents
|
||||
<ChevronDown
|
||||
size={16}
|
||||
className={cn(
|
||||
'transform transition-transform',
|
||||
isOpen && 'rotate-180',
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
|
||||
<ol
|
||||
className={cn(
|
||||
'mt-0.5 max-lg:absolute max-lg:top-full max-lg:mt-0 max-lg:w-full space-y-0 max-lg:bg-white max-lg:shadow',
|
||||
!isOpen && 'hidden lg:block',
|
||||
isOpen && 'block',
|
||||
)}
|
||||
>
|
||||
{toc.map((heading) => (
|
||||
<li key={heading.slug}>
|
||||
<a
|
||||
href={`#${heading.slug}`}
|
||||
className="text-sm text-gray-500 no-underline hover:text-black max-lg:block max-lg:border-b max-lg:px-3 max-lg:py-1"
|
||||
onClick={() => {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
{heading.text}
|
||||
</a>
|
||||
|
||||
{heading.children.length > 0 && (
|
||||
<ol className="my-0 ml-4 mt-1 max-lg:ml-0 max-lg:mt-0 max-lg:list-none space-y-0">
|
||||
{heading.children.map((children) => {
|
||||
return (
|
||||
<li key={children.slug}>
|
||||
<a
|
||||
href={`#${children.slug}`}
|
||||
className="text-sm text-gray-500 no-underline hover:text-black max-lg:block max-lg:border-b max-lg:px-3 max-lg:py-1 max-lg:pl-8"
|
||||
onClick={() => {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
{children.text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -299,7 +299,7 @@ Let’s take a look at some pros and cons for the last programming language on o
|
||||
|
||||
- Go’s ecosystem is quite young when compared to the other alternatives here, so the maturity of the tools available might not be the same as, for example, Java or JavaScript tooling.
|
||||
|
||||
## Choosing the Ideal Backend Language
|
||||
### Choosing the Ideal Backend Language
|
||||
|
||||
So, are these the best backend programming languages out there? Is there an absolute “best” backend programming language?
|
||||
|
||||
@@ -326,13 +326,13 @@ A handy tool when trying to evaluate a language like that is [roadmap.sh](https:
|
||||
|
||||
There you’ll find community-maintained roadmaps for many career paths within software development. In particular, for this article, the [backend roadmap](https://roadmap.sh/backend) is a great place to start, because while picking a backend language is important, you’ll see there that it’s not just about the language. In fact, there is a lot of tech around the language that is also required (I’m referring to databases, git, understanding how client-server communication works, and a big “etc).
|
||||
|
||||
## Jumpstarting Your Backend Development Journey
|
||||
### Jumpstarting Your Backend Development Journey
|
||||
|
||||
To get started with your backend development journey, it's crucial to have a roadmap that guides you through the learning process and equips you with the skills to build robust and scalable backend systems.
|
||||
|
||||
Lucky for you, if you’re reading this, that means you’ve found the most complete and comprehensive roadmap online: [roadmap.sh](https://roadmap.sh), the current [backend roadmap](https://roadmap.sh/backend) is filled with details of everything you should and could (optionally) learn in your journey to becoming a backend developer.
|
||||
|
||||
## Guided Learning: From Online Courses to Bootcamps
|
||||
### Guided Learning: From Online Courses to Bootcamps
|
||||
|
||||
Online courses and bootcamps serve as invaluable companions on your learning expedition. Platforms like Udemy, Coursera, and freeCodeCamp offer comprehensive backend development courses.
|
||||
|
||||
@@ -340,13 +340,13 @@ These resources not only cover programming languages like Python, Java, or JavaS
|
||||
|
||||
Whatever choice you go for, make sure you’re not following trends or just copying the learning methods of others. Learning is a very personal experience and what works for others might not work for you, and vice versa. So make sure to do the proper research and figure out what option works best for you.
|
||||
|
||||
## Building Community Connections for Learning Support
|
||||
### Building Community Connections for Learning Support
|
||||
|
||||
Joining developer communities (there are several on Twitter for example), forums like Stack Overflow, or participating in social media groups dedicated to backend development creates a network of support.
|
||||
|
||||
Engaging with experienced developers, sharing challenges, and seeking advice fosters a collaborative learning environment. Attend local meetups or virtual events if you can to connect with professionals in the field, gaining insights and building relationships that can prove invaluable throughout your journey.
|
||||
|
||||
## Think about you and your project
|
||||
### Think about you and your project
|
||||
|
||||
There are many ways to go about picking the ideal backend language for you. If there is anything you should take home with you after reading this article, it is that most languages are equivalent in the sense that you’ll be able to do pretty much everything with any of them.
|
||||
|
||||
@@ -359,7 +359,7 @@ The questions you should also be asking yourself are:
|
||||
|
||||
In the end, personal preference and actual project requirements (if you have any) are very important, because both will influence how much you enjoy (or don’t enjoy) the learning process.
|
||||
|
||||
## Crafting a Portfolio to Display Your Backend Skills:
|
||||
### Crafting a Portfolio to Display Your Backend Skills:
|
||||
|
||||
As you accumulate skills and knowledge, showcase your journey through a well-crafted portfolio. Include projects that highlight your backend skills, demonstrating your ability to - design databases, implement server-side logic, and integrate with client side technologies. Whether it's a dynamic web application, a RESTful API, or a data-driven project, your portfolio becomes a tangible representation of your backend development capabilities for potential employers or collaborators.
|
||||
|
||||
@@ -367,7 +367,7 @@ When it comes to deciding where to publish this portfolio, you have some options
|
||||
|
||||
In the end, the important thing is that you should be sharing your experience somewhere, especially when you don’t have working experience in the field.
|
||||
|
||||
## Conclusion
|
||||
### Conclusion
|
||||
|
||||
In the end, there are many backend programming languages to choose from, and what language you go for, is up to you and your particular context/needs. All I can do is guide you to the door, but you have to cross it yourself. Some interesting options are:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Chatty I/O
|
||||
# Chat I/O
|
||||
|
||||
The cumulative effect of a large number of I/O requests can have a significant impact on performance and responsiveness.
|
||||
|
||||
|
||||
@@ -87,33 +87,3 @@ export async function getGuideById(
|
||||
|
||||
return allGuides.find((guide) => guide.id === id);
|
||||
}
|
||||
|
||||
type HeadingType = ReturnType<MarkdownFileType['getHeadings']>[number];
|
||||
export type HeadingGroupType = HeadingType & { children: HeadingType[] };
|
||||
|
||||
const NUMBERED_LIST_REGEX = /^\d+\.\s+?/;
|
||||
|
||||
export function getGuideTableOfContent(headings: HeadingType[]) {
|
||||
const tableOfContents: HeadingGroupType[] = [];
|
||||
let currentGroup: HeadingGroupType | null = null;
|
||||
|
||||
headings
|
||||
.filter((heading) => heading.depth !== 1)
|
||||
.forEach((heading) => {
|
||||
if (heading.depth === 2) {
|
||||
currentGroup = {
|
||||
...heading,
|
||||
text: heading.text.replace(NUMBERED_LIST_REGEX, ''),
|
||||
children: [],
|
||||
};
|
||||
tableOfContents.push(currentGroup);
|
||||
} else if (currentGroup && heading.depth === 3) {
|
||||
currentGroup.children.push({
|
||||
...heading,
|
||||
text: heading.text.replace(NUMBERED_LIST_REGEX, ''),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return tableOfContents;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,8 @@ import { ActivityPage } from '../../components/Activity/ActivityPage';
|
||||
import AccountLayout from '../../layouts/AccountLayout.astro';
|
||||
---
|
||||
|
||||
<AccountLayout
|
||||
title='Activity'
|
||||
noIndex={true}
|
||||
initialLoadingMessage={'Loading activity'}
|
||||
>
|
||||
<AccountLayout title='Update Profile' noIndex={true} initialLoadingMessage={'Loading activity'}>
|
||||
<AccountSidebar activePageId='activity' activePageTitle='Activity'>
|
||||
<ActivityPage client:only='react' />
|
||||
<ActivityPage client:only="react" />
|
||||
</AccountSidebar>
|
||||
</AccountLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import GuideContent from '../../components/Guide/GuideContent.astro';
|
||||
import GuideHeader from '../../components/GuideHeader.astro';
|
||||
import MarkdownFile from '../../components/MarkdownFile.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getGuideById } from '../../lib/guide';
|
||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||
@@ -23,6 +23,11 @@ const ogImageUrl = getOpenGraphImageUrl({
|
||||
canonicalUrl={guideData.canonicalUrl}
|
||||
ogImageUrl={ogImageUrl}
|
||||
>
|
||||
<GuideHeader guide={guide!} />
|
||||
<GuideContent guide={guide!} />
|
||||
<GuideHeader guide={guide} />
|
||||
|
||||
<div class='mx-auto max-w-[700px] py-5 sm:py-10'>
|
||||
<MarkdownFile>
|
||||
<guide.Content />
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import GuideContent from '../../components/Guide/GuideContent.astro';
|
||||
import GuideHeader from '../../components/GuideHeader.astro';
|
||||
import MarkdownFile from '../../components/MarkdownFile.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getGuideById } from '../../lib/guide';
|
||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||
@@ -23,6 +23,11 @@ const ogImageUrl = getOpenGraphImageUrl({
|
||||
canonicalUrl={guideData.canonicalUrl}
|
||||
ogImageUrl={ogImageUrl}
|
||||
>
|
||||
<GuideHeader guide={guide!} />
|
||||
<GuideContent guide={guide!} />
|
||||
<GuideHeader guide={guide} />
|
||||
|
||||
<div class='mx-auto max-w-[700px] py-5 sm:py-10'>
|
||||
<MarkdownFile>
|
||||
<guide.Content />
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import GuideContent from '../../components/Guide/GuideContent.astro';
|
||||
import GuideHeader from '../../components/GuideHeader.astro';
|
||||
import MarkdownFile from '../../components/MarkdownFile.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getGuideById } from '../../lib/guide';
|
||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||
@@ -24,5 +24,10 @@ const ogImageUrl = getOpenGraphImageUrl({
|
||||
ogImageUrl={ogImageUrl}
|
||||
>
|
||||
<GuideHeader guide={guide!} />
|
||||
<GuideContent guide={guide!} />
|
||||
|
||||
<div class='mx-auto max-w-[700px] py-5 sm:py-10'>
|
||||
<MarkdownFile>
|
||||
<guide.Content />
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import GuideContent from '../../components/Guide/GuideContent.astro';
|
||||
import GuideHeader from '../../components/GuideHeader.astro';
|
||||
import MarkdownFile from '../../components/MarkdownFile.astro';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { getAllGuides, type GuideFileType } from '../../lib/guide';
|
||||
import { getOpenGraphImageUrl } from '../../lib/open-graph';
|
||||
@@ -38,5 +38,10 @@ const ogImageUrl = getOpenGraphImageUrl({
|
||||
ogImageUrl={ogImageUrl}
|
||||
>
|
||||
<GuideHeader guide={guide} />
|
||||
<GuideContent guide={guide!} />
|
||||
|
||||
<div class='mx-auto max-w-[700px] py-5 sm:py-10'>
|
||||
<MarkdownFile>
|
||||
<guide.Content />
|
||||
</MarkdownFile>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
Reference in New Issue
Block a user