Expose releases as an api (#4247)
This commit is contained in:
@ -6,7 +6,7 @@ import { compileMDX } from 'next-mdx-remote/rsc';
|
||||
import remarkBehead from 'remark-behead';
|
||||
import gfm from 'remark-gfm';
|
||||
|
||||
import { ReleaseNote } from '@/app/get-releases';
|
||||
import { ReleaseNote } from '@/app/releases/api/route';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 810px;
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import matter from 'gray-matter';
|
||||
|
||||
export interface ReleaseNote {
|
||||
slug: string;
|
||||
date: string;
|
||||
release: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
function compareSemanticVersions(a: string, b: string) {
|
||||
const a1 = a.split('.');
|
||||
const b1 = b.split('.');
|
||||
|
||||
const len = Math.min(a1.length, b1.length);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const a2 = +a1[i] || 0;
|
||||
const b2 = +b1[i] || 0;
|
||||
|
||||
if (a2 !== b2) {
|
||||
return a2 > b2 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return b1.length - a1.length;
|
||||
}
|
||||
|
||||
export async function getReleases(): Promise<ReleaseNote[]> {
|
||||
const files = fs.readdirSync('src/content/releases');
|
||||
const releasenotes: ReleaseNote[] = [];
|
||||
|
||||
for (const fileName of files) {
|
||||
if (!fileName.endsWith('.md') && !fileName.endsWith('.mdx')) {
|
||||
continue;
|
||||
}
|
||||
const file = fs.readFileSync(`src/content/releases/${fileName}`, 'utf-8');
|
||||
const { data: frontmatter, content } = matter(file);
|
||||
releasenotes.push({
|
||||
slug: fileName.slice(0, -4),
|
||||
date: frontmatter.Date,
|
||||
release: frontmatter.release,
|
||||
content: content,
|
||||
});
|
||||
}
|
||||
|
||||
releasenotes.sort((a, b) => compareSemanticVersions(b.release, a.release));
|
||||
|
||||
return releasenotes;
|
||||
}
|
||||
67
packages/twenty-website/src/app/releases/api/route.tsx
Normal file
67
packages/twenty-website/src/app/releases/api/route.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import { compareSemanticVersions } from '@/shared-utils/compareSemanticVersions';
|
||||
import fs from 'fs';
|
||||
import matter from 'gray-matter';
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
|
||||
export interface ReleaseNote {
|
||||
slug: string;
|
||||
date: string;
|
||||
release: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
|
||||
const BASE_URL = 'https://twenty.com/';
|
||||
|
||||
// WARNING: This API is used by twenty-front, not just by twenty-website
|
||||
// Make sure you don't change it without updating twenty-front at the same time
|
||||
export async function getReleases(baseUrl?: string): Promise<ReleaseNote[]> {
|
||||
const files = fs.readdirSync('src/content/releases');
|
||||
const releasenotes: ReleaseNote[] = [];
|
||||
|
||||
for (const fileName of files) {
|
||||
if (!fileName.endsWith('.md') && !fileName.endsWith('.mdx')) {
|
||||
continue;
|
||||
}
|
||||
const file = fs.readFileSync(`src/content/releases/${fileName}`, 'utf-8');
|
||||
const { data: frontmatter, content } = matter(file);
|
||||
|
||||
let updatedContent;
|
||||
if(baseUrl) {
|
||||
updatedContent = content.replace(/!\[(.*?)\]\((?!http)(.*?)\)/g, (match, alt, src) => {
|
||||
// Check if src is a relative path (not starting with http:// or https://)
|
||||
if (!src.startsWith('/')) {
|
||||
src = `${baseUrl}/${src}`;
|
||||
} else {
|
||||
src = `${baseUrl}${src}`;
|
||||
}
|
||||
return ``;
|
||||
});
|
||||
}
|
||||
|
||||
releasenotes.push({
|
||||
slug: fileName.slice(0, -4),
|
||||
date: frontmatter.Date,
|
||||
release: frontmatter.release,
|
||||
content: updatedContent ?? content,
|
||||
});
|
||||
}
|
||||
|
||||
releasenotes.sort((a, b) => compareSemanticVersions(b.release, a.release));
|
||||
|
||||
return releasenotes;
|
||||
}
|
||||
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
|
||||
const host = request.nextUrl.hostname;
|
||||
const protocol = request.nextUrl.protocol;
|
||||
const baseUrl = `${protocol}//${host}`;
|
||||
|
||||
console.log(baseUrl);
|
||||
|
||||
return NextResponse.json(await getReleases(baseUrl), { status: 200 })
|
||||
}
|
||||
@ -5,7 +5,7 @@ import { Line } from '@/app/_components/releases/Line';
|
||||
import { Release } from '@/app/_components/releases/Release';
|
||||
import { Title } from '@/app/_components/releases/StyledTitle';
|
||||
import { ContentContainer } from '@/app/_components/ui/layout/ContentContainer';
|
||||
import { getReleases } from '@/app/get-releases';
|
||||
import { getReleases } from '@/app/releases/api/route';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Twenty - Releases',
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
export function compareSemanticVersions(a: string, b: string) {
|
||||
const a1 = a.split('.');
|
||||
const b1 = b.split('.');
|
||||
|
||||
const len = Math.min(a1.length, b1.length);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const a2 = +a1[i] || 0;
|
||||
const b2 = +b1[i] || 0;
|
||||
|
||||
if (a2 !== b2) {
|
||||
return a2 > b2 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return b1.length - a1.length;
|
||||
}
|
||||
Reference in New Issue
Block a user