Commit fa07545c authored by Konrad Borowski's avatar Konrad Borowski

Initial commit

parents
Pipeline #107617524 passed with stages
in 1 minute and 56 seconds
{
"env": {
"test": {
"presets": [
["preact-cli/babel", { "modules": "commonjs" }]
]
}
}
}
node_modules
/build
/*.log
*.lock
package-lock.json
\ No newline at end of file
pages:
image: node:12
script:
- npm install
- node_modules/.bin/preact build --no-prerender
- mv build public
artifacts:
paths:
- public
only:
- master
# apibin
## CLI Commands
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# test the production build locally
npm run serve
# run tests with jest and preact-render-spy
npm run test
```
For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md).
{
"name": "apibin",
"short_name": "apibin",
"start_url": "/apibin",
"display": "browser",
"background_color": "#fff",
"theme_color": "#dddd66",
"icons": [
{
"src": "/assets/icons/android-chrome-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/assets/icons/android-chrome-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
]
}
{
"private": true,
"name": "apibin",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"start": "per-env",
"start:production": "npm run -s serve",
"start:development": "npm run -s dev",
"build": "preact build",
"serve": "preact build && serve build --single",
"dev": "preact watch",
"lint": "eslint src",
"test": "jest"
},
"eslintConfig": {
"extends": "eslint-config-synacor"
},
"eslintIgnore": [
"build/*"
],
"devDependencies": {
"enzyme": "^3.10.0",
"enzyme-adapter-preact-pure": "^2.0.0",
"eslint": "^6.0.1",
"eslint-config-synacor": "^3.0.4",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.9.0",
"jest-preset-preact": "^1.0.0",
"per-env": "^1.0.2",
"preact-cli": "^3.0.0-rc.7",
"preact-render-spy": "^1.2.1",
"serve": "^11.1.0"
},
"dependencies": {
"history": "^4.10.1",
"preact": "^10.0.1",
"preact-render-to-string": "^5.1.0",
"preact-router": "^3.0.0"
},
"jest": {
"preset": "jest-preset-preact",
"setupFiles": [
"<rootDir>/tests/__mocks__/browserMocks.js",
"<rootDir>/tests/__mocks__/setupTests.js"
]
}
}
export default function (config, env) {
config.output.publicPath = env.production ? '/apibin/' : ''
}
export default 'https://pastebin.run/';
import { h, Component } from 'preact';
import { Router } from 'preact-router';
import { createHashHistory } from 'history';
import Header from './header';
import Contents from './contents';
// Code-splitting is automated for routes
import Home from '../routes/home';
import About from '../routes/about';
import Paste from '../routes/paste';
export default class App extends Component {
handleRoute = e => {
this.currentUrl = e.url;
}
render() {
return (<div id="app">
<Header />
<Contents>
<Router onChange={this.handleRoute} history={createHashHistory()}>
<Home path="/" />
<About path="/about" />
<Paste path="/:id" />
</Router>
</Contents>
</div>);
}
}
import { h } from 'preact';
import style from './style.css';
const Contents = ({ children }) => (<div class={style.contents}>
{children}
</div>);
export default Contents;
.contents {
padding: 10px 20px;
}
import { h } from 'preact';
import { Link } from 'preact-router/match';
import style from './style.css';
const Header = () => (<header class={style.header}>
<h1><Link href="/">apibin</Link></h1>
<nav>
<Link activeClassName={style.active} href="/">Home</Link>
<Link activeClassName={style.active} href="/about">About</Link>
<a href="https://gitlab.com/pastebinrun/apibin">Source code</a>
</nav>
</header>);
export default Header;
.header {
height: 56px;
padding: 0;
background: #dd6;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
z-index: 50;
}
.header h1 {
float: left;
margin: 0;
padding: 0 15px;
line-height: 56px;
font-weight: 400;
}
.header nav {
float: right;
font-size: 100%;
}
.header nav a {
display: inline-block;
line-height: 56px;
padding: 0 15px;
}
.header nav a:hover, .header nav a:active {
background: rgba(0,0,0,0.05);
}
.header nav a.active {
background: rgba(0,0,0,0.1);
}
import './style';
import App from './components/app';
export default App;
import { h } from 'preact';
import style from './style';
const About = () => (<div class={style.about}>
<p>
This is a demonstration of <a href="https://pastebin.run">pastebin.run</a>'s API. Every API used by apibin is
public and can be used by both JavaScript applications
running in a web browser as well as applications running
outside of web browser (such as Discord bots).
</p>
<p>
If you are looking for a pastebin, please visit our main web site, <a href="https://pastebin.run/">pastebin.run</a>.
While this demo can be used as a pastebin, it requires
JavaScript (unlike the main pastebin), and it's much more
prone to breaking. And it's not because of APIs changing &mdash;
those are intended to remain stable, but rather because we
can change the routes at any time &mdash; this isn't a production
application.
</p>
<p>
Why does this exist? <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">Dogfooding</a>, essentially.
</p>
</div>);
export default About;
.about {
margin: 0 auto;
max-width: 600px;
}
import { h, Component } from 'preact';
import { route } from 'preact-router';
import style from './style';
import baseUrl from '../../base-url';
class Home extends Component {
state = {
code: '',
expiration: 'PT1H',
languages: [],
language: 'plaintext'
}
onLanguageChange = e => this.setState({ language: e.target.value })
onExpirationChange = e => this.setState({ expiration: e.target.value })
onTextareaChange = e => this.setState({ code: e.target.value })
onSubmit = async e => {
e.preventDefault();
const { code, expiration, language } = this.state;
const request = await fetch(`${baseUrl}api/v1/pastes`, {
body: new URLSearchParams({ code, expiration, language }),
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
route(`/${await request.text()}`);
}
async componentWillMount() {
this.setState({ languages: await (await fetch(`${baseUrl}api/v1/languages`)).json() });
}
render(_, { expiration, code, languages, language }) {
return (<form onSubmit={this.onSubmit}>
<label>
Language: <select value={language} onInput={this.onLanguageChange}>
{languages.map(({ identifier, name }) => <option value={identifier}>{name}</option>)}
</select>
</label> <label>
Expires: <select value={expiration} onInput={this.onExpirationChange}>
<option value="PT1H">1 hour</option>
<option value="P1D">1 day</option>
<option value="P7D">1 week</option>
<option>Never</option>
</select>
</label>
<button class={style.submit} type="submit">Share</button>
<textarea class={style.full} required onInput={this.onTextareaChange}>{code}</textarea>
</form>);
}
}
export default Home;
.submit {
float: right;
}
.full {
width: 100%;
height: 400px;
}
import { h, Component } from 'preact';
import style from './style';
import baseUrl from '../../base-url';
export default class Paste extends Component {
controller = new AbortController()
state = {
code: null
}
copyLink = e => {
e.preventDefault();
navigator.clipboard.writeText(this.getLink());
}
getLink() {
const { id } = this.props;
return `https://pastebin.run/${id}`;
}
async componentWillMount() {
const { id } = this.props;
const { signal } = this.controller;
this.setState({
code: await (await fetch(`${baseUrl}${id}.txt`, { signal })).text()
});
}
componentWillUnmount() {
this.controller.abort();
}
render(_, { code }) {
return (<div>
<div>Share link: <input readonly value={this.getLink()} /> <button onClick={this.copyLink}>Copy link</button></div>
{code === null ? 'Loading…' : <pre class={style.pre}><code>{code}</code></pre>}
</div>);
}
}
.pre {
background: #f5f2f0;
padding: 16px;
overflow: auto;
}
html, body {
padding: 0;
margin: 0;
font-family: sans-serif;
line-height: 1.6;
}
// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage
/**
* An example how to mock localStorage is given below 👇
*/
/*
// Mocks localStorage
const localStorageMock = (function() {
let store = {};
return {
getItem: (key) => store[key] || null,
setItem: (key, value) => store[key] = value.toString(),
clear: () => store = {}
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock
}); */
// This fixed an error related to the CSS and loading gif breaking my Jest test
// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets
module.exports = 'test-file-stub'
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-preact-pure'
configure({
adapter: new Adapter()
})
import { h } from 'preact'
import Header from '../src/components/header'
import { shallow } from 'enzyme'
describe('Header', () => {
test('Header contains site name', () => {
const header = shallow(<Header />)
expect(header.find('h1 Link').children().text()).toBe('apibin')
})
})
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment