Skip to content

Commit

Permalink
Merge branch 'feature/frontpage'
Browse files Browse the repository at this point in the history
  • Loading branch information
attakei committed Nov 22, 2024
2 parents ce2c369 + 28231b9 commit d5bfefd
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 45 deletions.
3 changes: 3 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const api = new Hono();
*/
api.post('/content-url', zValidator('json', ContentAddress), async (c) => {
const addr = c.req.valid('json');
if (addr.ref === '') {
addr.ref = undefined;
}
try {
// Try to fetch content as address validation.
const octokit = initClient(c);
Expand Down
32 changes: 32 additions & 0 deletions src/components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { FC } from 'hono/jsx';

export const Input: FC<{
name: string;
label: string;
required?: boolean;
}> = (props: {
name: string;
label: string;
required?: boolean;
}) => (
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label" for={`input-${props.name}`}>
{props.label}
</label>
</div>
<div class="field-body">
<div class="field">
<p class="control">
<input
class="input"
type="text"
id={`input-${props.name}`}
name={props.name}
required={props.required || false}
/>
</p>
</div>
</div>
</div>
);
138 changes: 138 additions & 0 deletions src/html.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* HTML rendering routes.
*
* This module is to manage routes for Web API (not render html and images).
*/
import { Hono } from 'hono';
import { Octokit } from '@octokit/rest';
import { fetchContent } from './client';
import { Input } from './components';
import { parseSlug } from './models';
import type { ZennContent } from './models';
import { parseContentMarkdown } from './parser';

const app = new Hono<{ Bindings: CloudflareBindings }>();
app.use(async (c, next) => {
c.setRenderer((content) => {
return c.html(`<!DOCTYPE html>${content}`);
});
await next();
});

/**
* Render frontpage that has form.
*/
app.get('/', (c) => {
return c.render(
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Zenn Private Previwer</title>
<script src="https://unpkg.com/[email protected]" />
<script src="https://unpkg.com/[email protected]/json-enc.js" />
<script src="https://unpkg.com/[email protected]/client-side-templates.js" />
<script src="https://unpkg.com/mustache@latest" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"
/>
</head>
<body>
<section class="hero">
<div class="hero-body">
<h1 class="title">Zenn Private Previwer</h1>
</div>
</section>
<section class="section">
<form
hx-post="/api/content-url"
hx-ext="json-enc, client-side-templates"
hx-target="#result"
hx-swap="innerHTML"
mustache-template="tmpl-result"
>
<Input name="owner" label="ユーザー/Org" />
<Input name="repo" label="リポジトリ" />
<Input name="path" label="ファイルパス" />
<Input name="ref" label="ブランチ" />
<div class="field is-horizontal">
<div class="field-label" />
<div class="field-body">
<div class="field">
<p class="control">
<button class="button is-success" type="submit">
URL生成
</button>
</p>
</div>
</div>
</div>
</form>
</section>
<section class="section" id="result" />
<template id="tmpl-result">
<article class="message is-link">
<div class="message-header">
<p>OK!</p>
</div>
<div class="message-body">
<p>Content URL is created!</p>
<p>
Link is{' '}
<a href="/{{slug}}" target="_blank" rel="noreferrer">
here
</a>
.
</p>
</div>
</article>
</template>
</body>
</html>,
);
});

app.get('/:slug', async (c) => {
let props: ZennContent;
const octokit = new Octokit({
auth: c.env.REPO_PAT,
});
const addr = parseSlug(c.req.param('slug'));
try {
const md = await fetchContent(octokit, addr);
props = parseContentMarkdown(md);
return c.render(
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Zenn article</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/lib/index.min.css"
/>
</head>
<body>
<div>
<h1>
{props.frontMatter?.title}
<br />
{props.frontMatter?.emoji}
</h1>
</div>
<hr />
<hr />
<div class="znc" dangerouslySetInnerHTML={{ __html: props.body }} />
</body>
</html>,
);
} catch (error) {
console.error(error);
if (error.status) {
c.status(404);
return c.text('Content is not found.');
}
}
});

export default app;
47 changes: 2 additions & 45 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { html, raw } from 'hono/html';
import { sentry } from '@hono/sentry';
import { Octokit } from '@octokit/rest';
import api from './api';
import htmlRouter from './html';
import { fetchContent } from './client';
import { parseContentMarkdown } from './parser';
import { parseSlug } from './models';
Expand All @@ -18,50 +19,6 @@ app.use('*', (c: Context, next: any) => {
})(c, next);
});
app.route('/api', api);

app.get('/:slug', async (c) => {
let props: ZennContent;
const octokit = new Octokit({
auth: c.env.REPO_PAT,
});
const addr = parseSlug(c.req.param('slug'));
try {
const md = await fetchContent(octokit, addr);
props = parseContentMarkdown(md);
return c.html(
html`
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Zenn article</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/lib/index.min.css"
/>
</head>
<body>
<div>
<h1>
${props.frontMatter?.title}
<br />
${props.frontMatter?.emoji}
</h1>
</div>
<hr />
<hr />
<div class="znc">${raw(props.body)}</div>
</body>
</html>
`,
);
} catch (error) {
console.error(error);
if (error.status) {
c.status(404);
return c.text('Content is not found.');
}
}
});
app.route('/', htmlRouter);

export default app;

0 comments on commit d5bfefd

Please sign in to comment.