Dokumentacija etherpad APIja, popravki, preurejen obrazec za urejanje

pull/26/head
Jurij Podgoršek 2023-12-16 01:53:05 +01:00
parent 1ae74fdcd4
commit 4423b69f3f
16 changed files with 109 additions and 59 deletions

View File

@ -1,15 +0,0 @@
# -*- restclient -*-
##########################
# YUFU API dokumentacija #
##########################
# Poganjamo z restclient major mode-om v emacsu
# Ustvari nov pad
POST https://yufu-manifest.ddev.site/etherpad-api/createPad?padID=d2d479bf-b3cb-455a-b7db-5d53c31d1fa2
Content-Type: application/x-www-form-urlencoded
text=While post-communist, post-socialist and post-Yugoslav discourses merely reinforce the appearance of an unchanging and unstable present with a more or less accurate expression of the situation, Yugofuturism follows the example of other ethnofuturist movements such as Afrofuturism, Sinofuturism, Baltic Ethnofuturism and Hungarofuturism, which tactically empower peripheral identities and subversively affirm individual cultural curiosities.
# Brisi vsebino pada
GET https://yufu-manifest.ddev.site/etherpad-api/deletePad?padID=d2d479bf-b3cb-455a-b7db-5d53c31d1fa2

View File

@ -0,0 +1 @@
../web/modules/custom/etherpad_api/doc/api.rest

View File

@ -0,0 +1 @@
../web/modules/custom/yufu_concept/tests/local-emacs-test.rest

View File

@ -2,4 +2,4 @@ BASE_URL="https://yufu-manifest.ddev.site"
JSONAPI_PATH="/jsonapi" JSONAPI_PATH="/jsonapi"
FILE_PATH="/sites/default/files" FILE_PATH="/sites/default/files"
ETHERPAD_URL="https://pisi.kompot.si" ETHERPAD_URL="https://pisi.kompot.si"
ETHERPAD_API_URL="https://yufu-manifest.ddev.site/etherpad-api" ETHERPAD_PREFIX="yufu-"

2
nuxt/.gitignore vendored
View File

@ -4,5 +4,5 @@ node_modules
.nitro .nitro
.cache .cache
.output .output
.env .env.local
dist dist

View File

@ -1,6 +1,6 @@
<script setup="setup"> <script setup="setup">
const { etherpadApiUrl, etherpadPrefix } = useRuntimeConfig().public const { etherpadApiUrl } = useEtherpadApi()
const prikazi = ref(false) const prikazi = ref(false)
const revisionId = ref(null) const revisionId = ref(null)
@ -9,8 +9,8 @@ const dodajPojem = async () => {
revisionId.value = crypto.randomUUID() revisionId.value = crypto.randomUUID()
// Ustvari nov, prazen pad // Ustvari nov, prazen pad
const padId = etherpadPrefix + revisionId.value const padId = revisionId.value
const resp = await $fetch(`${etherpadApiUrl}/createPad?padID=${padId}&text=`) const resp = await $fetch(`${etherpadApiUrl}/createPad?padID=${padId}`)
prikazi.value = true prikazi.value = true
} }

View File

@ -2,7 +2,7 @@
import { stripHtml } from 'string-strip-html' import { stripHtml } from 'string-strip-html'
const { etherpadApiUrl, etherpadPrefix } = useRuntimeConfig().public const { etherFetch } = useEtherpadApi()
const store = usePojmiStore() const store = usePojmiStore()
@ -20,36 +20,29 @@ const urejanje = ref(false)
const urediPojem = async () => { const urediPojem = async () => {
// Ustvari pad s tekstom pojma, ce se ne obstaja // Ustvari pad s tekstom pojma, ce se ne obstaja
const tekstPojma = stripHtml(pojem.value.tekst).result const tekstPojma = stripHtml(pojem.value.tekst).result
const resp = await $fetch(`${etherpadApiUrl}/createPad?padID=${etherpadPrefix + revisionId.value}`, { const resp = await etherFetch('/createPad', {
method: 'post', padID: revisionId.value,
body: {
text: tekstPojma text: tekstPojma
}
}) })
urejanje.value = true urejanje.value = true
} }
const brisiPojem = async () => {
const resp = await $fetch(`${etherpadApiUrl}/deletePad?padID=${etherpadPrefix + revisionId.value}`)
alert('pad izbrisan')
}
</script> </script>
<template> <template>
<section class="pojem"> <section class="pojem">
<PojemForm v-if="urejanje" :revisionId="revisionId" :pojem="pojem" />
<div v-else>
<div class="gumb" @click="urediPojem">Uredi</div>
<h2>{{ pojem.naslov }}</h2> <h2>{{ pojem.naslov }}</h2>
<div class="tekst" v-html="pojem.tekst" /> <div class="tekst" v-html="pojem.tekst" />
<div class="gumb" @click="urediPojem">Uredi</div>
<div class="gumb" @click="brisiPojem">Briši (pad)</div>
<div v-if="urejanje" class="pojem">
<PojemForm :revisionId="revisionId" />
</div> </div>
</section> </section>
</template> </template>
<style scoped> <style scoped>
.gumb { .gumb {
float: right;
margin-right: 1rem; margin-right: 1rem;
} }
</style> </style>

View File

@ -1,28 +1,30 @@
<script setup="setup"> <script setup="setup">
const { etherpadUrl, etherpadPrefix } = useRuntimeConfig().public const { etherFetch } = useEtherpadApi()
const store = usePojmiStore() const store = usePojmiStore()
const props = defineProps({ const props = defineProps({
naslov: String, revisionId: String,
revisionId: String pojem: Object,
urejanje: Object
}) })
//const pojem = computed(() => store.pojmi[props.naslov]) let naslov = ref(props.pojem.naslov)
let email = ref('')
//await store.naloziPojme() const oddajPredlog = async data => {
// @TODO vsebina pada v tekst, testirat
// const UREJAM = !!pojem.value const resp = await etherFetch('/getText', { padID: props.revisionId })
const UREJAM = false store.ustvariPojem({
title: naslov.value,
let naslov = '' email: email.value,
let tekst = '' text: resp.data.text,
let email = '' uuid: props.revisionId
})
const oddajPredlog = data => { alert("sprememba predlagana!")
console.log('oddajam predlog!', naslov, tekst, email) // @TODO vrni urejanje v navaden prikaz, ko je končano!
store.ustvariPojem(data) urejanje.set(false)
} }
const etherNalozen = ev => { const etherNalozen = ev => {
@ -33,7 +35,6 @@ const etherNalozen = ev => {
<template> <template>
<section class="pojem"> <section class="pojem">
<div v-if="UREJAM">UREJAM</div>
<form class="pojem" @submit.prevent="oddajPredlog"> <form class="pojem" @submit.prevent="oddajPredlog">
<label for="naslov">Naslov</label> <label for="naslov">Naslov</label>
<input name="naslov" type="text" v-model="naslov"> <input name="naslov" type="text" v-model="naslov">

View File

@ -0,0 +1,21 @@
export const useEtherpadApi = () => {
const { baseUrl } = useRuntimeConfig().public
const etherpadApiUrl = `${baseUrl}/etherpad-api`
const etherFetch = (path, params) => {
// Convert to form key-value pairs
const formBody = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join('&')
// Call API
return $fetch(etherpadApiUrl + path, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
body: formBody
})
}
return { etherpadApiUrl, etherFetch }
}

View File

@ -36,7 +36,6 @@ export default defineNuxtConfig({
baseUrl: process.env.BASE_URL, baseUrl: process.env.BASE_URL,
jsonapiPath: process.env.JSONAPI_PATH, jsonapiPath: process.env.JSONAPI_PATH,
etherpadUrl: process.env.ETHERPAD_URL, etherpadUrl: process.env.ETHERPAD_URL,
etherpadApiUrl: process.env.ETHERPAD_API_URL,
etherpadPrefix: process.env.ETHERPAD_PREFIX etherpadPrefix: process.env.ETHERPAD_PREFIX
} }
} }

View File

@ -19,7 +19,10 @@ export const usePojmiStore = defineStore('pojmi', {
const { baseUrl, headers, deserialize } = useApi() const { baseUrl, headers, deserialize } = useApi()
const req = await $fetch(`${baseUrl}/api/pojem/dodaj`, { const req = await $fetch(`${baseUrl}/api/pojem/dodaj`, {
headers, headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST", method: "POST",
body: JSON.stringify(data) body: JSON.stringify(data)
}) })

View File

@ -0,0 +1,20 @@
# Etherpad API
This module can be used to proxy requests to an etherpad API.
After installing the module, you must set the etherpad instance API URL and the API key.
Etherpad API documentation can be found here: https://etherpad.org/doc/v1.8.4/#index_http_api
The etherpad API proxy URL is /etherpad-api (@TODO make configurable). When calling it, you can omit the version part (e.g. /1) and the API key (which is set in the configuration). The pad ID prefix is hardcoded for the moment (so that requests are limited to specific pads, not the whole instance).
Some example API calls can found in [api.rest](doc/api.rest).
You can do either GET or POST requests to the proxy, passing parameters via query or post parameters. POST parameters must be passed as key-value pairs (application/x-www-form-urlencoded). The proxy always makes POST requests to etherpad, for the sake of simplicity. The API key is always added, the padID is always prefixed.
## TODO
- configurable PAD ID prefix
- configurable proxy API path
- API call whitelist
- security audit
- use pad groups instead of prefixed pads? (https://etherpad.org/doc/v1.8.4/#index_groups)

View File

@ -0,0 +1,26 @@
# -*- restclient -*-
##############################
# Etherpad API documentation #
##############################
# You can run these requests using the emacs restclient major mode
# Create new pad (with content)
POST https://yufu-manifest.ddev.site/etherpad-api/createPad?padID=d2d479bf-b3cb-455a-b7db-5d53c31d1fa2
Content-Type: application/x-www-form-urlencoded
text=While post-communist, post-socialist and post-Yugoslav discourses merely reinforce the appearance of an unchanging and unstable present with a more or less accurate expression of the situation, Yugofuturism follows the example of other ethnofuturist movements such as Afrofuturism, Sinofuturism, Baltic Ethnofuturism and Hungarofuturism, which tactically empower peripheral identities and subversively affirm individual cultural curiosities.
# Crate new pad (with content, JSON)
POST https://yufu-manifest.ddev.site/etherpad-api/createPad?padID=d2d479bf-b3cb-455a-b7db-5d53c31d1fa2
Content-Type: application/json
{
"text": "While post-communist, post-socialist and post-Yugoslav discourses merely reinforce the appearance of an unchanging and unstable present with a more or less accurate expression of the situation, Yugofuturism follows the example of other ethnofuturist movements such as Afrofuturism, Sinofuturism, Baltic Ethnofuturism and Hungarofuturism, which tactically empower peripheral identities and subversively affirm individual cultural curiosities."
}
# Fetch pad content
GET https://yufu-manifest.ddev.site/etherpad-api/getText?padID=d2d479bf-b3cb-455a-b7db-5d53c31d1fa2
# Delete pad content
GET https://yufu-manifest.ddev.site/etherpad-api/deletePad?padID=d2d479bf-b3cb-455a-b7db-5d53c31d1fa2

View File

@ -52,7 +52,7 @@ class Client {
/** /**
* Method description. * Method description.
*/ */
public function request($method, $url, $opts = []) { public function request($url, $opts = []) {
$uri = "{$this->baseUrl}/" . self::API_VERSION . '/' . explode('?', $url)[0]; $uri = "{$this->baseUrl}/" . self::API_VERSION . '/' . explode('?', $url)[0];
if (!isset($opts['form_params'])) { if (!isset($opts['form_params'])) {
$opts['form_params'] = []; $opts['form_params'] = [];

View File

@ -41,7 +41,7 @@ class EtherpadApiController extends ControllerBase {
$opts = ['form_params' => $data]; $opts = ['form_params' => $data];
try { try {
return $this->client->request($request->getMethod(), $uri, $opts); return $this->client->request($uri, $opts);
} catch (ClientException $exception) { } catch (ClientException $exception) {
return new Response($exception->getMessage(), $exception->getCode()); return new Response($exception->getMessage(), $exception->getCode());
} }

View File

@ -1,4 +1,4 @@
# _*_ restclient _*_ # -*- restclient -*-
# Makes post http request to a resource from yufu_concept module. # Makes post http request to a resource from yufu_concept module.
# Make sure uuid is uniq or it will create new revision. # Make sure uuid is uniq or it will create new revision.