main.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {
  2. html,
  3. render,
  4. } from 'https://unpkg.com/htm@latest/preact/index.mjs?module';
  5. import {
  6. useState,
  7. useEffect,
  8. } from 'https://unpkg.com/preact@latest/hooks/dist/hooks.module.js?module';
  9. function SiteForm({ create = false }) {
  10. const [newSite, setNewSite] = useState({});
  11. const [error, setError] = useState(null);
  12. const [siteKey, setSiteKey] = useState(null);
  13. const [siteUpdated, setSiteUpdated] = useState(false);
  14. const onChange = (att) => (e) => {
  15. setNewSite((prev) => ({ ...prev, [att]: e.target.value }));
  16. };
  17. const onClick = async (e) => {
  18. e.preventDefault();
  19. setSiteUpdated(false);
  20. setError(null);
  21. const result = await fetch(
  22. create ? '/_register/' : `/_register/${newSite.siteId}`,
  23. {
  24. method: create ? 'POST' : 'PATCH',
  25. headers: {
  26. Accept: 'application/json',
  27. 'Content-Type': 'application/json',
  28. },
  29. body: JSON.stringify(newSite),
  30. }
  31. );
  32. if (result.status === 400) {
  33. const { message } = await result.json();
  34. setError(message);
  35. return;
  36. }
  37. if (result.status === 403) {
  38. const { message } = await result.json();
  39. setError(message);
  40. return;
  41. }
  42. if (result.status === 404) {
  43. const { message } = await result.json();
  44. setError(message);
  45. return;
  46. }
  47. if (result.status >= 300) {
  48. setError('Unknown error, try again later...');
  49. return;
  50. }
  51. setNewSite({});
  52. if (create) {
  53. const { key } = await result.json();
  54. setSiteKey(key);
  55. } else {
  56. setSiteUpdated(true);
  57. }
  58. };
  59. return html`<div class="card">
  60. <header>
  61. <h2>${create ? 'Create new site' : 'Update site'}</h2>
  62. </header>
  63. <div class="content">
  64. <form>
  65. <div class="field">
  66. <label>
  67. Site Id:
  68. <input value=${newSite.siteId || ''} onChange=${onChange('siteId')}
  69. /></label>
  70. <p class="help-text">Only letters and '_' are accepted.</p>
  71. </div>
  72. <div class="field">
  73. <label>
  74. Name:
  75. <input value=${newSite.name || ''} onChange=${onChange('name')}
  76. /></label>
  77. <p class="help-text">This name will appears in sent email.</p>
  78. </div>
  79. <div class="field">
  80. <label>
  81. Email from:
  82. <input
  83. value=${newSite.emailFrom || ''}
  84. onChange=${onChange('emailFrom')}
  85. />
  86. </label>
  87. <p class="help-text">
  88. All sent email for this site will have this origin.
  89. </p>
  90. </div>
  91. ${create &&
  92. html`<div class="field">
  93. <label>
  94. Owner:
  95. <input value=${newSite.owner || ''} onChange=${onChange('owner')} />
  96. </label>
  97. <p class="help-text">
  98. This is the site owner email. Confirmation links are sent to this
  99. address.
  100. </p>
  101. </div>`}
  102. </form>
  103. ${error && html`<p class="text-error">${error}</p>`}
  104. ${siteKey &&
  105. html`<p class="text-success">
  106. Success! Please, save the site encryption key:
  107. <input value=${siteKey} onChange=${(e) => e.preventDefault()} />
  108. this is the last opportunity to read it.
  109. </p>
  110. <p class="text-success">
  111. Now you must confirm the site creation before using it, you will
  112. receive an email at the 'owner' address with a confirmation link.
  113. </p>`}
  114. ${siteUpdated &&
  115. html` <p class="text-success">
  116. Success! Now you must confirm the site update by visiting the
  117. confirmation link we have just sent to the owner email.
  118. </p>`}
  119. </div>
  120. <footer>
  121. <button
  122. class="button primary"
  123. onClick=${onClick}
  124. disabled=${!newSite.siteId}
  125. >
  126. ${create ? 'Create site' : 'Update site'}
  127. </button>
  128. </footer>
  129. </div>`;
  130. }
  131. function App() {
  132. const [settings, setSettings] = useState({});
  133. useEffect(() => {
  134. let mounted = true;
  135. const updateRegistration = async () => {
  136. const result = await (await fetch('/site/settings')).json();
  137. console.log(result);
  138. if (mounted) setSettings(result);
  139. };
  140. updateRegistration();
  141. return () => (mounted = false);
  142. }, []);
  143. return html`<div class="container">
  144. <div class="row">
  145. <div class="col-4" />
  146. <div class="col"><h1>Ricochet.js admin</h1></div>
  147. </div>
  148. ${settings.registrationEnabled &&
  149. html`<div class="row">
  150. <div class="col-2" />
  151. <div class="col-4">
  152. <${SiteForm} create />
  153. </div>
  154. <div class="col-4">
  155. <${SiteForm} />
  156. </div>
  157. </div>`}
  158. ${settings.registrationEnabled === false &&
  159. html`<div class="row">
  160. <div class="col-4" />
  161. <div class="col">
  162. <h2 class="text-error">Site registration is disabled</h2>
  163. </div>
  164. </div>`}
  165. </div>`;
  166. }
  167. render(html`<${App} />`, document.body);