You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

647 lines
25 KiB

  1. {% extends "base.html" %}
  2. {% load compress static hc_extras %}
  3. {% block title %}Project Settings - {{ project }}{% endblock %}
  4. {% block content %}
  5. {% with project.transfer_request as transfer_request %}
  6. <div class="row">
  7. <div class="col-sm-9 col-md-6">
  8. {% for message in messages %}
  9. <p class="alert alert-{{ message.tags }}">{{ message }}</p>
  10. {% endfor %}
  11. {% if transfer_request and transfer_request.user == request.user %}
  12. {% with can_accept=transfer_request.can_accept %}
  13. <div id="transfer-request" class="panel">
  14. <div class="panel-body settings-block">
  15. <h2>Ownership Transfer Request</h2>
  16. <p>
  17. <strong>{{ project.owner.email }}</strong> would like to transfer
  18. the ownership of this project to you.
  19. </p>
  20. {% if not can_accept %}
  21. {% with num_checks=project.num_checks num_available=request.profile.num_checks_available %}
  22. <p>
  23. This project has
  24. <strong>{{ num_checks }} check{{ num_checks|pluralize}}</strong>,
  25. but your account only has space for
  26. <strong>{{ num_available }} additional check{{ num_available|pluralize }}</strong>.
  27. To accept the transfer, please
  28. <a href="{% url 'hc-billing' %}">upgrade your account first!</a>
  29. </p>
  30. {% endwith%}
  31. {% endif %}
  32. <div class="pull-right">
  33. <form method="post">
  34. {% csrf_token %}
  35. <button
  36. type="submit"
  37. name="reject_transfer"
  38. class="btn btn-default">Reject</button>
  39. <button
  40. type="submit"
  41. name="accept_transfer"
  42. {% if not can_accept %}disabled{% endif %}
  43. class="btn btn-primary">Accept</button>
  44. </form>
  45. </div>
  46. </div>
  47. </div>
  48. {% endwith %}
  49. {% endif %}
  50. <div class="panel panel-{{ project_name_status|default:'default' }}">
  51. <div class="panel-body settings-block">
  52. <h2>Project Name</h2>
  53. {{ project }}
  54. {% if rw %}
  55. <a
  56. href="#"
  57. class="btn btn-default pull-right"
  58. data-toggle="modal"
  59. data-target="#set-project-name-modal">Change Project Name</a>
  60. {% endif %}
  61. </div>
  62. {% if project_name_updated %}
  63. <div class="panel-footer">
  64. Project name updated
  65. </div>
  66. {% endif %}
  67. </div>
  68. {% if rw %}
  69. <div class="panel panel-{{ api_status|default:'default' }}">
  70. <div class="panel-body settings-block">
  71. <h2>API Access</h2>
  72. <table id="api-keys" class="table">
  73. <tr>
  74. <td>API key</td>
  75. <td>
  76. {% if project.api_key %}
  77. {% if show_keys %}
  78. <code>{{ project.api_key }}</code>
  79. {% else %}
  80. <code>{{ project.api_key|mask_key }}</code>
  81. {% endif %}
  82. {% else %}
  83. <span class="not-set">not set</span>
  84. {% endif %}
  85. </td>
  86. <td class="text-right">
  87. {% if project.api_key %}
  88. <a href="#"
  89. data-revoke-key="api_key"
  90. data-name="API key">Revoke</a>
  91. {% else %}
  92. <a href="#" data-create-key="api_key">Create</a>
  93. {% endif %}
  94. </td>
  95. </tr>
  96. <tr>
  97. <td>API key (read-only)</td>
  98. <td>
  99. {% if project.api_key_readonly %}
  100. {% if show_keys %}
  101. <code>{{ project.api_key_readonly }}</code>
  102. {% else %}
  103. <code>{{ project.api_key_readonly|mask_key }}</code>
  104. {% endif %}
  105. {% else %}
  106. <span class="not-set">not set</span>
  107. {% endif %}
  108. </td>
  109. <td class="text-right">
  110. {% if project.api_key_readonly %}
  111. <a href="#"
  112. data-revoke-key="api_key_readonly"
  113. data-name="read-only API key">Revoke</a>
  114. {% else %}
  115. <a href="#" data-create-key="api_key_readonly">Create</a>
  116. {% endif %}
  117. </td>
  118. </tr>
  119. <tr>
  120. <td>Ping key</td>
  121. <td>
  122. {% if project.ping_key %}
  123. {% if show_keys %}
  124. <code>{{ project.ping_key }}</code>
  125. {% else %}
  126. <code>{{ project.ping_key|mask_key }}</code>
  127. {% endif %}
  128. {% else %}
  129. <span class="not-set">not set</span>
  130. {% endif %}
  131. </td>
  132. <td class="text-right">
  133. {% if project.ping_key %}
  134. <a href="#"
  135. data-revoke-key="ping_key"
  136. data-name="Ping key">Revoke</a>
  137. {% else %}
  138. <a href="#" data-create-key="ping_key">Create</a>
  139. {% endif %}
  140. </td>
  141. </tr>
  142. </table>
  143. <p>See also:</p>
  144. <ul>
  145. <li><a href="{% url 'hc-serve-doc' 'api' %}">API documentation</a></li>
  146. {% if project.api_key_readonly and show_keys %}
  147. {% if enable_prometheus %}
  148. <li>
  149. <a href="{% url 'hc-metrics' project.code project.api_key_readonly %}">Prometheus metrics endpoint</a>
  150. </li>
  151. {% endif %}
  152. <li>
  153. <a href="{{ project.dashboard_url }}">Read-only dashboard</a>
  154. (<a href="https://github.com/healthchecks/dashboard/#security">security considerations</a>)
  155. </li>
  156. {% endif %}
  157. </ul>
  158. {% if not show_keys %}
  159. {% if project.api_key or project.api_key_readonly or project.ping_key %}
  160. <form method="post">
  161. {% csrf_token %}
  162. <button
  163. type="submit"
  164. name="show_keys"
  165. class="btn btn-default pull-right">Show Keys</button>
  166. </form>
  167. {% endif %}
  168. {% endif %}
  169. </div>
  170. {% if key_created %}
  171. <div class="panel-footer">
  172. Key created
  173. </div>
  174. {% endif %}
  175. {% if key_revoked %}
  176. <div class="panel-footer">
  177. Key revoked
  178. </div>
  179. {% endif %}
  180. </div>
  181. {% endif %}
  182. {% with invite_suggestions=project.invite_suggestions %}
  183. <div class="panel panel-{{ team_status|default:'default' }}">
  184. <div class="panel-body settings-block">
  185. <h2>Team Access</h2>
  186. {% if memberships or invite_suggestions %}
  187. <table id="team-table" class="table">
  188. <tr>
  189. <th>Email</th>
  190. <th>Role</th>
  191. <th></th>
  192. </tr>
  193. <tr>
  194. <td class="email">{{ project.owner.email }}</td>
  195. <td>Owner</td>
  196. <td></td>
  197. </tr>
  198. {% for m in memberships %}
  199. <tr>
  200. <td class="email">{{ m.user.email }}</td>
  201. <td>{{ m.get_role_display }}</td>
  202. <td>
  203. {% if is_manager and m.user != request.user %}
  204. <a
  205. href="#"
  206. data-email="{{ m.user.email }}"
  207. class="pull-right member-remove">Remove</a>
  208. {% endif %}
  209. </td>
  210. </tr>
  211. {% endfor %}
  212. {% if is_manager and invite_suggestions %}
  213. <tr id="suggestions-row">
  214. <td colspan="3">
  215. Add Users from Other Projects
  216. </td>
  217. </tr>
  218. {% for user in invite_suggestions %}
  219. <tr class="invite-suggestion">
  220. <td>{{ user.email }} </td>
  221. <td></td>
  222. <td>
  223. <a
  224. href="#"
  225. data-email="{{ user.email }}"
  226. class="pull-right add-to-team">Add to Team</a>
  227. </td>
  228. </tr>
  229. {% endfor %}
  230. {% endif %}
  231. </table>
  232. {% else %}
  233. <p>
  234. <strong>Invite team members to your project.</strong>
  235. Share access to your checks and configured integrations
  236. without having to share login details.
  237. </p>
  238. {% endif %}
  239. <br />
  240. {% if is_manager %}
  241. {% if project.can_invite_new_users %}
  242. <a
  243. href="#"
  244. class="btn btn-primary pull-right"
  245. data-toggle="modal"
  246. data-target="#invite-team-member-modal">Invite a Team Member</a>
  247. {% else %}
  248. <div class="alert alert-info">
  249. <strong>Team size limit reached.</strong>
  250. {% if is_owner %}
  251. To invite new members by email, please
  252. <a href="{% url 'hc-pricing' %}">upgrade your account!</a>
  253. {% else %}
  254. To invite new members, please ask project's owner
  255. to upgrade their account.
  256. {% endif %}
  257. </div>
  258. {% endif %}
  259. {% endif %}
  260. </div>
  261. {% endwith %}
  262. {% if team_member_invited %}
  263. <div class="panel-footer">
  264. {{ team_member_invited }} invited to team
  265. </div>
  266. {% endif %}
  267. {% if team_member_duplicate %}
  268. <div class="panel-footer">
  269. {{ team_member_duplicate }} is already a member
  270. </div>
  271. {% endif %}
  272. {% if team_member_removed %}
  273. <div class="panel-footer">
  274. {{ team_member_removed }} removed from team
  275. </div>
  276. {% endif %}
  277. </div>
  278. {% if is_owner %}
  279. <div class="panel panel-{{ transfer_status|default:'default' }}">
  280. <div class="panel-body settings-block">
  281. <h2>Transfer Ownership</h2>
  282. {% if transfer_request %}
  283. <form method="post">
  284. {% csrf_token %}
  285. <button
  286. type="submit"
  287. name="cancel_transfer"
  288. class="btn btn-default pull-right">Cancel Transfer</button>
  289. </form>
  290. Transfer initiated, awaiting confirmation from
  291. <strong>{{ transfer_request.user.email }}</strong>.
  292. {% else %}
  293. <a href="#"
  294. class="btn btn-default pull-right"
  295. data-toggle="modal"
  296. data-target="#transfer-modal">Transfer Project&hellip;</a>
  297. Transfer this project to a team member.
  298. {% endif %}
  299. </div>
  300. {% if transfer_initiated %}
  301. <div class="panel-footer">
  302. Transfer initiated!
  303. </div>
  304. {% endif %}
  305. {% if transfer_cancelled %}
  306. <div class="panel-footer">
  307. Transfer cancelled!
  308. </div>
  309. {% endif %}
  310. </div>
  311. {% endif %}
  312. {% if is_owner %}
  313. <div class="panel panel-default">
  314. <div class="panel-body settings-block">
  315. {% csrf_token %}
  316. <h2>Remove Project</h2>
  317. <a href="#"
  318. id="remove-project"
  319. class="btn btn-default pull-right"
  320. data-toggle="modal"
  321. data-target="#remove-project-modal">Remove Project</a>
  322. This will permanently remove project {{ project }}.
  323. <form action="{% url 'hc-remove-project' project.code %}" method="post">
  324. </form>
  325. </div>
  326. </div>
  327. {% endif %}
  328. </div>
  329. </div>
  330. {% if rw %}
  331. <form id="create-key-form" method="post">
  332. {% csrf_token %}
  333. <input id="create-key-type" type="hidden" name="create_key">
  334. </form>
  335. {% endif %}
  336. {% if rw %}
  337. <div id="revoke-key-modal" class="modal">
  338. <div class="modal-dialog">
  339. <form id="revoke-key-form" method="post">
  340. {% csrf_token %}
  341. <input id="revoke-key-type" type="hidden" name="revoke_key">
  342. <div class="modal-content">
  343. <div class="modal-header">
  344. <button type="button" class="close" data-dismiss="modal">&times;</button>
  345. <h4>Revoke <span class="name"></span>?</h4>
  346. </div>
  347. <div class="modal-body">
  348. <p>
  349. You are about to revoke your current
  350. <span class="name"></span>.
  351. </p>
  352. <p>
  353. Afterwards, you can generate a new key, but there will
  354. be <strong>no way of getting the key's current value back</strong>.
  355. </p>
  356. <p>Are you sure?</p>
  357. </div>
  358. <div class="modal-footer">
  359. <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  360. <button type="submit" class="btn btn-danger">
  361. Revoke <span class="name"></span>
  362. </button>
  363. </div>
  364. </div>
  365. </form>
  366. </div>
  367. </div>
  368. {% endif %}
  369. {% if is_manager %}
  370. <div id="remove-team-member-modal" class="modal">
  371. <div class="modal-dialog">
  372. <form id="remove-team-member-form" method="post">
  373. {% csrf_token %}
  374. <div class="modal-content">
  375. <div class="modal-header">
  376. <button type="button" class="close" data-dismiss="modal">&times;</button>
  377. <h4>Remove Team Member</h4>
  378. </div>
  379. <div class="modal-body">
  380. <p>You are about to remove <strong id="rtm-email"></strong> from the project.</p>
  381. <p>Are you sure?</p>
  382. <input
  383. type="hidden"
  384. name="email"
  385. id="remove-team-member-email" />
  386. </div>
  387. <div class="modal-footer">
  388. <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  389. <button
  390. type="submit"
  391. name="remove_team_member"
  392. class="btn btn-danger">Remove Member from Project</button>
  393. </div>
  394. </div>
  395. </form>
  396. </div>
  397. </div>
  398. {% endif %}
  399. {% if is_manager %}
  400. <div id="invite-team-member-modal" class="modal">
  401. <div class="modal-dialog">
  402. <form method="post" class="form-horizontal">
  403. {% csrf_token %}
  404. <input type="hidden" name="invite_team_member" value="1" />
  405. <div class="modal-content">
  406. <div class="modal-header">
  407. <button type="button" class="close" data-dismiss="modal">&times;</button>
  408. <h4>Invite a Team Member</h4>
  409. </div>
  410. <div class="modal-body">
  411. <div class="form-group">
  412. <label for="itm-email" class="col-sm-3 control-label">Email</label>
  413. <div class="col-sm-8">
  414. <input
  415. type="email"
  416. class="form-control"
  417. id="itm-email"
  418. name="email"
  419. maxlength="254"
  420. placeholder="[email protected]">
  421. </div>
  422. </div>
  423. <div class="form-group">
  424. <label class="col-sm-3 control-label">Access Level</label>
  425. <div class="col-sm-8">
  426. <label class="radio-container">
  427. <input
  428. type="radio"
  429. name="role"
  430. value="w"
  431. checked>
  432. <span class="radiomark"></span>
  433. Team Member
  434. <span class="help-block">
  435. Can create and manage checks and integrations.
  436. Cannot access your account's billing settings.
  437. </span>
  438. </label>
  439. <label class="radio-container">
  440. <input
  441. type="radio"
  442. name="role"
  443. value="m">
  444. <span class="radiomark"></span>
  445. Manager
  446. <span class="help-block">
  447. Same as Team Member, plus can invite and remove
  448. other members.
  449. </span>
  450. </label>
  451. <label class="radio-container">
  452. <input
  453. type="radio"
  454. name="role"
  455. value="r">
  456. <span class="radiomark"></span>
  457. Read-only
  458. <span class="help-block">
  459. Can view checks and integrations, but cannot
  460. modify anything. Cannot access project's API keys.
  461. </span>
  462. </label>
  463. </div>
  464. </div>
  465. </div>
  466. <div class="modal-footer">
  467. <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  468. <button
  469. type="submit"
  470. name="invite_team_member"
  471. class="btn btn-primary">Send Invite</button>
  472. </div>
  473. </div>
  474. </form>
  475. </div>
  476. </div>
  477. {% endif %}
  478. {% if rw %}
  479. <div id="set-project-name-modal" class="modal">
  480. <div class="modal-dialog">
  481. <form method="post" class="form-horizontal">
  482. {% csrf_token %}
  483. <div class="modal-content">
  484. <div class="modal-header">
  485. <button type="button" class="close" data-dismiss="modal">&times;</button>
  486. <h4>Change Project Name</h4>
  487. </div>
  488. <div class="modal-body">
  489. <div class="form-group">
  490. <label for="project-name" class="col-sm-4 control-label">Project Name</label>
  491. <div class="col-sm-7">
  492. <input
  493. type="text"
  494. class="form-control"
  495. id="project-name"
  496. name="name"
  497. maxlength="60"
  498. value="{{ project }}">
  499. </div>
  500. </div>
  501. </div>
  502. <div class="modal-footer">
  503. <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  504. <button
  505. type="submit"
  506. name="set_project_name"
  507. class="btn btn-primary">Set Project Name</button>
  508. </div>
  509. </div>
  510. </form>
  511. </div>
  512. </div>
  513. {% endif %}
  514. {% if is_owner %}
  515. <div id="remove-project-modal" class="modal">
  516. <div class="modal-dialog">
  517. <form method="post" action="{% url 'hc-remove-project' project.code %}">
  518. {% csrf_token %}
  519. <div class="modal-content">
  520. <div class="modal-header">
  521. <button type="button" class="close" data-dismiss="modal">&times;</button>
  522. <h4>Remove "{{ project }}"?</h4>
  523. </div>
  524. <div class="modal-body">
  525. <p>Danger zone! You are about to permanently remove
  526. project <strong>{{ project }}</strong> and all
  527. of its associated checks and integrations. Are you sure?
  528. </p>
  529. </div>
  530. <div class="modal-footer">
  531. <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  532. <button
  533. type="submit"
  534. class="btn btn-danger">Remove Project</button>
  535. </div>
  536. </div>
  537. </form>
  538. </div>
  539. </div>
  540. {% endif %}
  541. {% if is_owner and not transfer_request %}
  542. <div id="transfer-modal" class="modal">
  543. <div class="modal-dialog">
  544. <form
  545. class="form-horizontal"
  546. method="post">
  547. {% csrf_token %}
  548. <input type="hidden" name="transfer_project" value="1" />
  549. <div class="modal-content">
  550. <div class="modal-header">
  551. <button type="button" class="close" data-dismiss="modal">&times;</button>
  552. <h4>Transfer Ownership</h4>
  553. </div>
  554. <div class="modal-body">
  555. {% if memberships %}
  556. <div class="form-group">
  557. <label for="new-owner" class="col-sm-4 control-label">
  558. Choose owner
  559. </label>
  560. <div class="col-sm-7">
  561. <select
  562. id="new-owner"
  563. name="email"
  564. title="Select..."
  565. class="form-control selectpicker">
  566. {% for m in memberships %}
  567. <option>{{ m.user.email }}</option>
  568. {% endfor %}
  569. </select>
  570. </div>
  571. </div>
  572. {% else %}
  573. <p>
  574. This project currently has no team members.
  575. To transfer the ownership of this project, please start by
  576. inviting the new owner as a team member.
  577. </p>
  578. {% endif %}
  579. </div>
  580. <div class="modal-footer">
  581. <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  582. <button
  583. id="transfer-confirm"
  584. disabled
  585. type="submit"
  586. class="btn btn-primary">Initiate Transfer</button>
  587. </div>
  588. </div>
  589. </form>
  590. </div>
  591. </div>
  592. {% endif %}
  593. {% endwith %}
  594. {% endblock %}
  595. {% block scripts %}
  596. {% compress js %}
  597. <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
  598. <script src="{% static 'js/bootstrap.min.js' %}"></script>
  599. <script src="{% static 'js/bootstrap-select.min.js' %}"></script>
  600. <script src="{% static 'js/project.js' %}"></script>
  601. {% endcompress %}
  602. {% endblock %}