Skip to content

add: llms.txt content management #103

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

Open
wants to merge 2 commits into
base: main
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@ Once you create your SEO component in the edit view inside the Content Manager,
- Manage the meta title & description of your content and preview it in SERP.
- Manage your meta social tags (Facebook & Twitter) and preview your post.
- Analyze your content a little deeper via an SEO analysis of your content.

## LLMS.txt

Since version 2.0.9, a new LLMS field is available on the main page of the plugin. This field has been added for you to manage the content of your llms.txt file. An endpoint is also accessible (`/api/seo/llms`) for your **frontend** to fetch the content of this field and to create the llms.txt in order to have an AI-readable content for your website. It offers a practical solution to help AI systems better understand and utilize web content, particularly for technical documentation and APIs.
59 changes: 58 additions & 1 deletion admin/src/components/HomePage/Main/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,49 @@ import {
Tr,
Td,
Tabs,
Textarea,
Button,
} from '@strapi/design-system';
import { Plus } from '@strapi/icons';
import { Plus, Upload } from '@strapi/icons';

import { Illo } from './EmptyComponentLayout/illo';
import { SettingsModal } from './SettingsModal';

import { useLLMSApi } from '../../../hooks/useLLMSApi';

import { getTrad } from '../../../utils/getTrad';

export const Main = ({ contentTypes }) => {
const { getLLMSContent, setLLMSContent } = useLLMSApi();

const { formatMessage } = useIntl();
const [LLMSData, setLLMSData] = React.useState(null);

React.useEffect(() => {
const fetchLLMSContent = async () => {
const {
data: { content },
} = await getLLMSContent();

setLLMSData(content);
};

fetchLLMSContent();
}, []);

const handleChange = (event) => {
setLLMSData(event.target.value);
};

const handleSubmit = async () => {
await setLLMSContent({ content: LLMSData });
};

const TabValues = {
collectionTypes: 'collection-types',
singleTypes: 'single-types',
plugins: 'plugins',
llms: 'llms',
};

return (
Expand Down Expand Up @@ -60,6 +88,14 @@ export const Main = ({ contentTypes }) => {
})}
</Typography>
</Tabs.Trigger>
<Tabs.Trigger value={TabValues.llms}>
<Typography>
{formatMessage({
id: getTrad('SEOPage.tab.llms-title'),
defaultMessage: 'LLMS',
})}
</Typography>
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value={TabValues.collectionTypes}>
{/* TABLE */}
Expand Down Expand Up @@ -271,6 +307,27 @@ export const Main = ({ contentTypes }) => {

{/* END TABLE */}
</Tabs.Content>
<Tabs.Content value={TabValues.llms}>
{/* TABLE */}
<Box
padding={8}
hasRadius
borderColor="neutral150"
borderWidth="1px"
background="neutral0"
>
<Textarea
placeholder="This is a content placeholder"
name="content"
value={LLMSData}
onChange={handleChange}
/>
<Button onClick={handleSubmit} startIcon={<Upload />} marginTop="4">
Save
</Button>
</Box>
{/* END TABLE */}
</Tabs.Content>
</Tabs.Root>
</Box>
);
Expand Down
19 changes: 19 additions & 0 deletions admin/src/hooks/useLLMSApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useFetchClient } from '@strapi/strapi/admin';

export const useLLMSApi = () => {
const { get, post } = useFetchClient();

const getLLMSContent = async () => {
const resultData = await get('/seo/llms');

return resultData;
};

const setLLMSContent = async (data) => {
const resultData = await post('/seo/llms', data);

return resultData;
};

return { getLLMSContent, setLLMSContent };
};
2 changes: 2 additions & 0 deletions server/src/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { seoController } from './seo';
import { settingsController } from './settings';
import { llmsController } from './llms';

export default {
seo: seoController,
settings: settingsController,
llms: llmsController,
};
25 changes: 25 additions & 0 deletions server/src/controllers/llms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const llmsController = ({ strapi }) => {
const llmsService = strapi.plugins['seo'].services.llms;

const getContent = async (ctx) => {
try {
return llmsService.getLLMSContent();
} catch (err) {
ctx.throw(500, err);
}
};
const setContent = async (ctx) => {
const { body } = ctx.request;
try {
await llmsService.setLLMSContent(body);
return llmsService.getLLMSContent();
} catch (err) {
ctx.throw(500, err);
}
};

return {
getContent,
setContent,
};
};
1 change: 1 addition & 0 deletions server/src/controllers/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const settingsController = ({ strapi }) => {
ctx.throw(500, err);
}
};

return {
getSettings,
setSettings,
Expand Down
53 changes: 53 additions & 0 deletions server/src/routes/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export default {
type: 'admin',
routes: [
{
method: 'GET',
path: '/component',
handler: 'seo.findSeoComponent',
config: {
policies: [],
},
},
{
method: 'POST',
path: '/component',
handler: 'seo.createSeoComponent',
config: {
policies: [],
},
},
{
method: 'GET',
path: '/content-types',
handler: 'seo.findContentTypes',
config: {
policies: [],
},
},
{
method: 'GET',
path: '/settings',
handler: 'settings.getSettings',
config: { policies: [] },
},
{
method: 'POST',
path: '/settings',
handler: 'settings.setSettings',
config: { policies: [] },
},
{
method: 'GET',
path: '/llms',
handler: 'llms.getContent',
config: { policies: [] },
},
{
method: 'POST',
path: '/llms',
handler: 'llms.setContent',
config: { policies: [] },
},
],
};
11 changes: 11 additions & 0 deletions server/src/routes/content-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
type: 'content-api',
routes: [
{
method: 'GET',
path: '/llms',
handler: 'llms.getContent',
config: { policies: [] },
},
],
};
8 changes: 4 additions & 4 deletions server/src/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { seoRoutes } from './seo';
import { settingsRoutes } from './settings';
import admin from './admin';
import contentApi from './content-api';

export default {
seo: seoRoutes,
settings: settingsRoutes,
admin,
'content-api': contentApi,
};
30 changes: 0 additions & 30 deletions server/src/routes/seo.js

This file was deleted.

18 changes: 0 additions & 18 deletions server/src/routes/settings.js

This file was deleted.

2 changes: 2 additions & 0 deletions server/src/services/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { seoService } from './seo';
import { settingsService } from './settings';
import { llmsService } from './llms';

export default {
seo: seoService,
settings: settingsService,
llms: llmsService,
};
29 changes: 29 additions & 0 deletions server/src/services/llms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const llmsService = ({ strapi }) => {
const getPluginStore = () => {
return strapi.store({
environment: '',
type: 'plugin',
name: 'seo',
});
};

const getLLMSContent = async () => {
const pluginStore = getPluginStore();

const content = await pluginStore.get({ key: 'llms' });
return content;
};

const setLLMSContent = async (data) => {
const { content } = data;

const pluginStore = getPluginStore();
await pluginStore.set({ key: 'llms', value: { content } });
return pluginStore.get({ key: 'llms' });
};

return {
getLLMSContent,
setLLMSContent,
};
};