1#![doc = include_str!("../README.md")]
2#![warn(clippy::all, clippy::pedantic)]
3#![allow(clippy::missing_errors_doc, clippy::cast_sign_loss)]
4
5use bytes::{Bytes, BytesMut};
6use flexbuffers::VectorReader;
7use hashbrown::HashMap;
8use ordinary_auth::Auth;
9use ordinary_config::{
10 ContentDefinition, ModelConfig, QueryExpression, StoredCachePolicy, TemplateConfig,
11 TemplateRef, TemplateRefField, TemplateRefFieldBind,
12};
13use ordinary_storage::{ArtifactKind, CacheRead, RefDepth, Storage};
14use ordinary_types::{Field, Kind};
15use std::str::FromStr;
16use std::sync::Arc;
17use std::time::Duration;
18use std::{collections::BTreeMap, error::Error};
19use tracing::{info_span, instrument};
20
21use parking_lot::{Mutex, RwLock};
22use wasi_common::{
23 WasiCtx,
24 sync::{WasiCtxBuilder, add_to_linker},
25};
26use wasmtime::{
27 AsContext, AsContextMut, Caller, Extern, Linker, Memory, MemoryType, Module, Store,
28};
29
30pub use wasmtime::Engine;
31
32#[inline]
33fn read_wasm_mem(
34 caller: &mut Caller<'_, WasiCtx>,
35 ptr: i32,
36 buf: &mut [u8],
37) -> Result<(), Box<dyn Error>> {
38 let Some(Extern::Memory(mem)) = caller.get_export("memory") else {
39 return Err("no mem".into());
40 };
41
42 mem.read(caller.as_context_mut(), ptr as usize, buf)?;
43 Ok(())
44}
45
46#[inline]
47fn write_wasm_mem(caller: &mut Caller<'_, WasiCtx>, bytes: &[u8]) -> Option<i64> {
48 let alloc = match caller.get_export("alloc") {
49 Some(Extern::Func(malloc)) => match malloc.typed::<i32, i32>(caller.as_context()) {
50 Ok(malloc) => malloc,
51 Err(_) => return None,
52 },
53 _ => return None,
54 };
55
56 let len = bytes.len();
57
58 let Ok(params) = i32::try_from(len) else {
59 return None;
60 };
61
62 let Ok(ptr) = alloc.call(caller.as_context_mut(), params) else {
63 return None;
64 };
65
66 let Some(Extern::Memory(mem)) = caller.get_export("memory") else {
67 return None;
68 };
69
70 match mem.write(caller.as_context_mut(), ptr as usize, bytes) {
71 Ok(()) => {}
72 Err(_) => return None,
73 }
74
75 let ptr64 = i64::from(ptr) << 32;
76 let Ok(len64) = i64::try_from(len) else {
77 return None;
78 };
79
80 Some(ptr64 | len64)
81}
82
83#[derive(Clone, Debug)]
84enum Include {
85 Field(u8, Kind),
86 Fields(u8, Vec<Include>, bool),
87}
88
89impl Include {
90 fn build_for_field(
91 field: &Field,
92 ref_field: &TemplateRefField,
93 model_map: &HashMap<String, ModelConfig>,
94 ref_depth: &mut RefDepth,
95 ) -> Result<Self, Box<dyn Error>> {
96 let field_idx = field.idx;
97
98 match &ref_field.fields {
99 Some(nested_ref_fields) => {
100 let mut nested = vec![];
101 let mut is_list = false;
102
103 let mut nested_field_map = HashMap::new();
104
105 match &field.kind {
106 Kind::List { kind } => {
107 if let Kind::Object { name: _, fields } = &**kind {
108 for nested_field in fields {
109 nested_field_map.insert(nested_field.name.clone(), nested_field);
110 }
111
112 is_list = true;
113 } else {
114 return Err("cannot have nested fields on non object".into());
115 }
116 }
117 Kind::Object { name: _, fields } => {
118 for nested_field in fields {
119 nested_field_map.insert(nested_field.name.clone(), nested_field);
120 }
121 }
122 Kind::Ref {
123 model,
124 field: _,
125 many: _,
126 } => {
127 if let Some(model) = model_map.get(model) {
128 for nested_field in &model.fields {
129 nested_field_map.insert(nested_field.name.clone(), nested_field);
130 }
131
132 ref_depth.0.push(((field.idx, 1, None), RefDepth(vec![])));
133 }
134 }
135 _ => return Err("cannot have nested fields on non object".into()),
136 }
137
138 let mut fields_clone = nested_ref_fields.clone();
139 fields_clone.sort_by(|a, b| a.idx.cmp(&b.idx));
140
141 for nested_ref_field in fields_clone {
142 if let Some(nested_field) = nested_field_map.get(&nested_ref_field.name) {
143 nested.push(Self::build_for_field(
144 nested_field,
145 &nested_ref_field,
146 model_map,
147 ref_depth,
148 )?);
149 }
150 }
151
152 nested.shrink_to_fit();
153 Ok(Include::Fields(field_idx, nested, is_list))
154 }
155 None => Ok(Include::Field(field_idx, field.kind.clone())),
156 }
157 }
158
159 fn build_for_content(
160 content_def: &ContentDefinition,
161 template_ref: &TemplateRef,
162 ) -> Result<Vec<Self>, Box<dyn Error>> {
163 let mut include_fields = vec![];
164
165 let mut fields_clone = template_ref.fields.clone();
166 fields_clone.sort_by(|a, b| a.idx.cmp(&b.idx));
167
168 let mut content_def_field_map = HashMap::new();
169 for field in &content_def.fields {
170 content_def_field_map.insert(field.name.clone(), field);
171 }
172
173 for field in fields_clone {
174 if let Some(def_field) = content_def_field_map.get(&field.name) {
175 let include = Self::build_for_field(
176 def_field,
177 &field,
178 &HashMap::new(),
179 &mut RefDepth(vec![]),
180 )?;
181 include_fields.push(include);
182 }
183 }
184
185 include_fields.shrink_to_fit();
186 Ok(include_fields)
187 }
188
189 fn build_for_model(
190 model_config: &ModelConfig,
191 template_ref: &TemplateRef,
192 model_map: &HashMap<String, ModelConfig>,
193 ) -> Result<(Vec<Self>, RefDepth), Box<dyn Error>> {
194 let mut include_fields = vec![];
195
196 let mut fields_clone = template_ref.fields.clone();
197 fields_clone.sort_by(|a, b| a.idx.cmp(&b.idx));
198
199 let mut model_config_field_map = HashMap::new();
200 for field in &model_config.fields {
201 model_config_field_map.insert(field.name.clone(), field);
202 }
203 let mut ref_depth = RefDepth(vec![]);
204
205 for field in fields_clone {
206 if let Some(def_field) = model_config_field_map.get(&field.name) {
207 let include = Self::build_for_field(def_field, &field, model_map, &mut ref_depth)?;
208 include_fields.push(include);
209 }
210 }
211
212 include_fields.shrink_to_fit();
213 Ok((include_fields, ref_depth))
214 }
215
216 fn marry(
217 &self,
218 builder: &mut flexbuffers::VectorBuilder,
219 reader: &VectorReader<&[u8]>,
220 ) -> Result<(), Box<dyn Error>> {
221 match self {
222 Include::Field(idx, kind) => {
223 kind.copy_to(&reader.idx(*idx as usize), builder)?;
224 }
225 Include::Fields(idx, fields, is_list) => {
226 let mut nested = builder.start_vector();
227 let reader = reader.idx(*idx as usize).as_vector();
228
229 if *is_list {
230 for list_reader in &reader {
231 let mut builder = nested.start_vector();
232
233 for field in fields {
234 field.marry(&mut builder, &list_reader.as_vector())?;
235 }
236
237 builder.end_vector();
238 }
239 } else {
240 for field in fields {
241 field.marry(&mut nested, &reader)?;
242 }
243 }
244
245 nested.end_vector();
246 }
247 }
248
249 Ok(())
250 }
251}
252
253#[derive(Clone, Debug)]
254enum QueryKind {
255 Segment(usize, bool),
257 Token(u8),
259 None,
261}
262
263#[derive(Clone, Debug)]
264enum Query {
265 Model(
267 u8,
268 u8,
269 Kind,
270 RefDepth,
271 Option<ordinary_storage::QueryExpression>,
272 ),
273 Content(u8, u8, Kind),
275
276 ContentMulti(u8),
278}
279
280#[derive(Clone)]
281pub struct Template {
282 pub idx: u8,
283 pub config: TemplateConfig,
284
285 pub cache_control: Option<String>,
286
287 engine: Engine,
288 module: Arc<RwLock<Option<Module>>>,
289
290 storage: Arc<Storage>,
291
292 queries: Option<Vec<(Query, QueryKind, Vec<Include>)>>,
293}
294
295impl Template {
296 #[allow(clippy::too_many_lines, clippy::too_many_arguments)]
297 #[instrument(
298 name = "template"
299 skip(src, engine, config, auth, storage, model_map, content_map),
300 fields(i, nm),
301 err
302 )]
303 pub fn new(
304 src: Option<Bytes>,
305
306 config: TemplateConfig,
307 auth: Arc<Auth>,
308 model_map: &HashMap<String, ModelConfig>,
309 content_map: &HashMap<String, ContentDefinition>,
310
311 engine: Engine,
312 storage: Arc<Storage>,
313 ) -> Result<Template, Box<dyn Error>> {
314 tracing::Span::current().record("i", config.idx);
315 tracing::Span::current().record("nm", &config.name);
316
317 tracing::info!("init");
318
319 let module = if let Some(src) = src
320 && let Ok(module) = Module::new(&engine, src)
321 {
322 Arc::new(RwLock::new(Some(module)))
323 } else {
324 Arc::new(RwLock::new(None))
325 };
326
327 let mut queries = vec![];
328
329 if let Some(content_refs) = &config.content {
332 for content_ref in content_refs {
333 if let Some(content_def) = content_map.get(&content_ref.name) {
334 let mut def_fields_map = BTreeMap::new();
335
336 for field in &content_def.fields {
337 def_fields_map.insert(field.name.clone(), field);
338 }
339
340 let include_fields = Include::build_for_content(content_def, content_ref)?;
341
342 if content_ref.all.is_some() {
343 queries.push((
344 content_ref.idx,
345 Query::ContentMulti(content_def.idx),
346 QueryKind::None,
347 include_fields.clone(),
348 ));
349 }
350
351 for ref_field in &content_ref.fields {
352 if let Some(def_field) = def_fields_map.get(&ref_field.name)
353 && let Some(binding) = &ref_field.bind
354 {
355 match binding {
356 TemplateRefFieldBind::Segment {
357 name,
358 expression: _,
359 } => {
360 let mut path_pos = None;
361
362 for (i, segment) in config.route.split('/').enumerate() {
363 if segment == format!("{{{name}}}") {
364 path_pos = Some((i, name.starts_with('*')));
365 }
366 }
367
368 if let Some((path_pos, is_wildcard)) = path_pos {
369 queries.push((
370 content_ref.idx,
371 Query::Content(
372 content_def.idx,
373 def_field.idx,
374 def_field.kind.clone(),
375 ),
376 QueryKind::Segment(path_pos, is_wildcard),
377 include_fields.clone(),
378 ));
379 } else {
380 return Err(format!("cannot bind to segment {{{name}}} as it is not present in the route").into());
381 }
382 }
383 TemplateRefFieldBind::Token {
384 field,
385 expression: _,
386 } => {
387 let mut auth_field_idx = None;
388
389 for auth_field in &auth.config.access_token.claims {
390 if &auth_field.name == field {
391 auth_field_idx = Some(auth_field.idx);
392 }
393 }
394
395 if let Some(idx) = auth_field_idx {
396 queries.push((
397 content_ref.idx,
398 Query::Content(
399 content_def.idx,
400 def_field.idx,
401 def_field.kind.clone(),
402 ),
403 QueryKind::Token(idx),
404 include_fields.clone(),
405 ));
406 } else {
407 return Err(
408 format!("auth field {field} does not exist").into()
409 );
410 }
411 }
412 }
413 }
414 }
415 }
416 }
417 }
418
419 if let Some(model_refs) = &config.models {
420 for model_ref in model_refs {
421 if let Some(model_config) = model_map.get(&model_ref.name) {
422 let mut model_config_fields_map = BTreeMap::new();
423
424 let mut model_config = (*model_config).clone();
425
426 let uuid_field = Field {
427 idx: 0,
428 name: "uuid".into(),
429 kind: Kind::Uuid,
430 indexed: Some(true),
431 queryable: None,
432 searchable: None,
433 mapping: None,
434 doc: None,
435 encrypted: None,
436 compressed: None,
437 };
438
439 let mut new_fields = model_config.fields.clone();
440 new_fields.splice(0..0, vec![uuid_field]);
441
442 model_config.fields = new_fields;
443
444 for field in &model_config.fields {
445 model_config_fields_map.insert(field.name.clone(), field);
446 }
447
448 let (include_fields, ref_depth) =
449 Include::build_for_model(&model_config, model_ref, model_map)?;
450
451 for ref_field in &model_ref.fields {
452 if let Some(config_field) = model_config_fields_map.get(&ref_field.name)
453 && let Some(binding) = &ref_field.bind
454 {
455 match binding {
456 TemplateRefFieldBind::Segment { name, expression } => {
457 let mut path_pos = None;
458
459 for (i, segment) in config.route.split('/').enumerate() {
460 if segment == format!("{{{name}}}") {
461 path_pos = Some((i, name.starts_with('*')));
462 }
463 }
464
465 let exp = match expression {
466 Some(exp) => match exp {
467 QueryExpression::Gte => {
468 Some(ordinary_storage::QueryExpression::Gte)
469 }
470 QueryExpression::Lte => {
471 Some(ordinary_storage::QueryExpression::Lte)
472 }
473 QueryExpression::Gt => {
474 Some(ordinary_storage::QueryExpression::Gt)
475 }
476 QueryExpression::Lt => {
477 Some(ordinary_storage::QueryExpression::Lt)
478 }
479 QueryExpression::Eq => {
480 Some(ordinary_storage::QueryExpression::Eq)
481 }
482 QueryExpression::BeginsWith => {
483 Some(ordinary_storage::QueryExpression::BeginsWith)
484 }
485 },
486 None => None,
487 };
488
489 if let Some((path_pos, is_wildcard)) = path_pos {
490 queries.push((
491 model_ref.idx,
492 Query::Model(
493 model_config.idx,
494 config_field.idx,
495 config_field.kind.clone(),
496 ref_depth.clone(),
497 exp,
498 ),
499 QueryKind::Segment(path_pos, is_wildcard),
500 include_fields.clone(),
501 ));
502 } else {
503 return Err(format!("cannot bind to segment {{{name}}} as it is not present in the route").into());
504 }
505 }
506 TemplateRefFieldBind::Token { field, expression } => {
507 let mut auth_field_idx = None;
508
509 for auth_field in &auth.config.access_token.claims {
510 if &auth_field.name == field {
511 auth_field_idx = Some(auth_field.idx);
512 }
513 }
514
515 let exp = match expression {
516 Some(exp) => match exp {
517 QueryExpression::Gte => {
518 Some(ordinary_storage::QueryExpression::Gte)
519 }
520 QueryExpression::Lte => {
521 Some(ordinary_storage::QueryExpression::Lte)
522 }
523 QueryExpression::Gt => {
524 Some(ordinary_storage::QueryExpression::Gt)
525 }
526 QueryExpression::Lt => {
527 Some(ordinary_storage::QueryExpression::Lt)
528 }
529 QueryExpression::Eq => {
530 Some(ordinary_storage::QueryExpression::Eq)
531 }
532 QueryExpression::BeginsWith => {
533 Some(ordinary_storage::QueryExpression::BeginsWith)
534 }
535 },
536 None => None,
537 };
538
539 if let Some(idx) = auth_field_idx {
540 queries.push((
541 model_ref.idx,
542 Query::Model(
543 model_config.idx,
544 config_field.idx,
545 config_field.kind.clone(),
546 ref_depth.clone(),
547 exp,
548 ),
549 QueryKind::Token(idx),
550 include_fields.clone(),
551 ));
552 } else {
553 return Err(
554 format!("auth field {field} does not exist").into()
555 );
556 }
557 }
558 }
559 }
560 }
561 }
562 }
563 }
564
565 let mut cache_control = String::new();
566
567 if let Some(cache) = &config.cache
568 && let Some(http_cache) = &cache.http
569 && let Some(http_cache_control) = &http_cache.cache_control
570 {
571 http_cache_control.header_value(&mut cache_control, "")?;
573 } else {
574 }
576
577 Ok(Template {
578 idx: config.idx,
579 config,
580
581 cache_control: if cache_control.is_empty() {
582 None
583 } else {
584 Some(cache_control)
585 },
586
587 engine,
588 module,
589 storage,
590
591 queries: if queries.is_empty() {
592 None
593 } else {
594 queries.sort_by(|a, b| a.0.cmp(&b.0));
595 let mut queries = queries
596 .iter()
597 .map(|(_, q, qk, i)| (q.clone(), qk.clone(), i.clone()))
598 .collect::<Vec<_>>();
599
600 queries.shrink_to_fit();
601
602 Some(queries)
603 },
604 })
605 }
606
607 pub fn start_tasks(&self) {
608 if let Some(cache_config) = &self.config.cache
609 && let Some(stored_cache) = &cache_config.stored
610 {
611 let storage_clone = self.storage.clone();
612 let storage_clone2 = self.storage.clone();
613
614 let cache_config_clone = stored_cache.clone();
615 let idx = self.config.idx;
616
617 let cache_span = info_span!("cache");
618 let cache_span_clone = cache_span.clone();
619
620 let (mut sync_min, mut sync_max) =
622 cache_config_clone.sync_interval.unwrap_or((60, 60 * 3));
623
624 sync_min *= 1000;
625 sync_max *= 1000;
626
627 tokio::spawn(async move {
628 loop {
629 let cache_sync_interval_ms = rand::random_range(sync_min..sync_max);
632
633 tokio::time::sleep(Duration::from_millis(cache_sync_interval_ms)).await;
634
635 cache_span.in_scope(|| {
636 if let Err(err) = storage_clone.cache.sync_cache(&CacheRead::Template, idx)
637 {
638 tracing::error!(%err, "failed to sync cache");
639 }
640 });
641 }
642 });
643
644 if let StoredCachePolicy::Permanent = stored_cache.policy {
645 } else {
647 let (mut clean_min, mut clean_max) =
649 cache_config_clone.clean_interval.unwrap_or((60, 60 * 3));
650
651 clean_min *= 1000;
652 clean_max *= 1000;
653
654 tokio::spawn(async move {
655 loop {
656 let cache_clean_interval_ms = rand::random_range(clean_min..clean_max);
659
660 tokio::time::sleep(Duration::from_millis(cache_clean_interval_ms)).await;
661
662 cache_span_clone.in_scope(|| {
663 if let Err(err) = storage_clone2.cache.clean_cache(
664 &CacheRead::Template,
665 &cache_config_clone,
666 idx,
667 ) {
668 tracing::error!(%err, "failed to clean cache");
669 }
670 });
671 }
672 });
673 }
674 }
675 }
676
677 #[allow(clippy::too_many_lines)]
679 #[instrument(skip(self, path, params, error, totp, claims), err)]
680 pub fn query(
681 &self,
682 path: String,
683 params: Option<String>,
684 error: Option<(String, u16)>,
686 totp: Option<(String, String, String)>,
688 claims: &Option<VectorReader<&[u8]>>,
689 ) -> Result<Bytes, Box<dyn Error>> {
690 let mut builder = flexbuffers::Builder::new(&flexbuffers::BuilderOptions::SHARE_NONE);
691 let mut vec_builder = builder.start_vector();
692
693 if let Some(queries) = &self.queries {
694 let segments: Vec<&str> = path.split('/').collect();
695
696 let _params_map = match params {
697 Some(_params) => {
698 let params_map: BTreeMap<String, String> = BTreeMap::new();
699 Some(params_map)
701 }
702 None => None,
703 };
704
705 for (query, query_kind, includes) in queries {
710 let kind = match query {
711 Query::Model(_, _, kind, _, _) | Query::Content(_, _, kind) => kind,
712 Query::ContentMulti(_) => &Kind::Void,
713 };
714
715 let indexed_val: Bytes = match query_kind {
716 QueryKind::Segment(pos, is_wildcard) => {
717 if pos <= &segments.len() {
718 let val = if *is_wildcard {
719 let mut out = String::new();
720
721 for (i, segment) in segments.iter().enumerate() {
722 if i == *pos {
723 (*segment).clone_into(&mut out);
724 } else if i > *pos {
725 out = format!("{out}/{segment}");
726 }
727 }
728
729 out
730 } else {
731 segments[*pos].to_string()
732 };
733
734 match kind {
735 Kind::Uuid => {
736 let uuid = uuid::Uuid::from_str(&val)?;
737 Bytes::copy_from_slice(uuid.as_bytes())
738 }
739 _ => Bytes::copy_from_slice(val.as_bytes()),
740 }
741 } else {
742 return Err("position out of bounds for route".into());
743 }
744 }
745 QueryKind::Token(field_idx) => {
746 if let Some(claims) = claims {
747 let reader = claims.idx(*field_idx as usize);
748
749 match kind {
750 Kind::Uuid => Bytes::copy_from_slice(reader.as_blob().0),
751 Kind::String => Bytes::copy_from_slice(reader.as_str().as_bytes()),
752 _ => Bytes::new(),
753 }
754 } else {
755 return Err("no claims on query".into());
756 }
757 }
758 QueryKind::None => Bytes::new(),
759 };
760
761 let span = info_span!("storage");
762
763 match query {
764 Query::Content(def_idx, field_idx, _kind) => {
765 let content_obj = span.in_scope(|| {
766 self.storage
767 .content
768 .get_content(*def_idx, *field_idx, &indexed_val)
769 })?;
770
771 let root = flexbuffers::Reader::get_root(&content_obj[..])?;
772
773 let mut fields_builder = vec_builder.start_vector();
774
775 for include in includes {
776 include.marry(&mut fields_builder, &root.as_vector())?;
777 }
778
779 fields_builder.end_vector();
780 }
781 Query::ContentMulti(def_idx) => {
782 let content_objs =
783 span.in_scope(|| self.storage.content.list_content(*def_idx))?;
784
785 let root = flexbuffers::Reader::get_root(&content_objs[..])?;
786
787 let mut object_vector = vec_builder.start_vector();
788
789 for object in &root.as_vector() {
790 let obj_bytes = object.as_blob().0;
791
792 let object = flexbuffers::Reader::get_root(obj_bytes)?;
793
794 let mut fields_builder = object_vector.start_vector();
795
796 for include in includes {
797 include.marry(&mut fields_builder, &object.as_vector())?;
798 }
799
800 fields_builder.end_vector();
801 }
802
803 object_vector.end_vector();
804 }
805 Query::Model(model_kind, field_idx, _kind, ref_depth, expression) => {
806 if let Some(exp) = expression {
807 let items = span.in_scope(|| {
808 self.storage.model.query_model(
809 *model_kind,
810 *field_idx,
811 exp.as_byte(),
812 &indexed_val,
813 ref_depth,
814 None,
815 )
816 })?;
817
818 let root = flexbuffers::Reader::get_root(&items[..])?;
819
820 let mut item_builder = vec_builder.start_vector();
821
822 for item in &root.as_vector() {
823 let mut fields_builder = item_builder.start_vector();
824
825 for include in includes {
826 include.marry(&mut fields_builder, &item.as_vector())?;
827 }
828
829 fields_builder.end_vector();
830 }
831
832 item_builder.end_vector();
833 } else {
834 let model_item = span.in_scope(|| {
835 self.storage.model.get_model(
836 *model_kind,
837 *field_idx,
838 &indexed_val,
839 ref_depth,
840 None,
841 )
842 })?;
843
844 let root = flexbuffers::Reader::get_root(&model_item[..])?;
845
846 let mut fields_builder = vec_builder.start_vector();
847
848 for include in includes {
849 include.marry(&mut fields_builder, &root.as_vector())?;
850 }
851
852 fields_builder.end_vector();
853 }
854 }
855 }
856 }
857 }
858
859 if let Some(error) = error {
860 let mut error_builder = vec_builder.start_vector();
861
862 error_builder.push(error.0.as_str());
863 error_builder.push(error.1);
864
865 error_builder.end_vector();
866 }
867
868 if let Some(totp) = totp {
869 let mut totp_builder = vec_builder.start_vector();
870
871 totp_builder.push(totp.0.as_str());
872 totp_builder.push(totp.1.as_str());
873
874 let mut recovery_codes_vec = totp_builder.start_vector();
875
876 let mut chunk = String::new();
877
878 for (i, c) in totp.2.chars().enumerate() {
879 if i != 0 && i % 11 == 0 {
880 recovery_codes_vec.push(chunk.as_str());
881 chunk = String::new();
882 } else {
883 chunk = format!("{chunk}{c}");
884 }
885 }
886
887 recovery_codes_vec.end_vector();
888
889 totp_builder.end_vector();
890 }
891
892 vec_builder.end_vector();
893
894 Ok(Bytes::copy_from_slice(builder.view()))
895 }
896
897 #[instrument(skip(self, src), err)]
898 pub fn set_wasm(&self, src: &[u8]) -> Result<(), Box<dyn Error>> {
899 self.storage
900 .artifact
901 .put_artifact(self.idx, ArtifactKind::Template, src)?;
902
903 self.storage
904 .cache
905 .artifact_evict(CacheRead::Template, self.idx)?;
906
907 let mut lock = self.module.write();
908 let module = Module::new(&self.engine, src)?;
909
910 *lock = Some(module);
911 drop(lock);
912
913 Ok(())
914 }
915
916 #[instrument(skip(self, path, params, error, totp, claims), err)]
917 pub fn render(
918 &self,
919 path: String,
920 params: Option<String>,
921 error: Option<(String, u16)>,
923 totp: Option<(String, String, String)>,
925 claims: &Option<VectorReader<&[u8]>>,
926 ) -> Result<Bytes, Box<dyn Error>> {
927 let args = self.query(path, params, error, totp, claims)?;
928
929 let mut linker = Linker::new(&self.engine);
930 add_to_linker(&mut linker, |s| s)?;
931
932 let wasi = WasiCtxBuilder::new().build();
933 let mut store = Store::new(&self.engine, wasi);
934
935 let memory_ty = MemoryType::new(1, None);
936 Memory::new(&mut store, memory_ty)?;
937
938 let args = args.to_vec();
939
940 linker.func_wrap(
941 "env",
942 "host_get_input",
943 move |mut caller: Caller<'_, WasiCtx>| -> i64 {
944 let args = args.clone();
945 if let Some(res) = write_wasm_mem(&mut caller, &args) {
946 return res;
947 }
948
949 0
950 },
951 )?;
952
953 let out = Arc::new(Mutex::new(Bytes::new()));
954 let out_clone = Arc::clone(&out);
955
956 linker.func_wrap(
957 "env",
958 "host_set_output",
959 move |mut caller: Caller<'_, WasiCtx>, ptr: i32, len: i32| -> i32 {
960 let mut buf = BytesMut::zeroed(len as usize);
962
963 if read_wasm_mem(&mut caller, ptr, &mut buf).is_ok() {
964 let mut out = out_clone.lock();
965 *out = buf.into();
966
967 drop(out);
968
969 return 1;
970 }
971
972 0
973 },
974 )?;
975
976 let lock = self.module.read();
977
978 if let Some(module) = &*lock {
979 linker.module(&mut store, "", module)?;
980 }
981
982 drop(lock);
983
984 if linker
985 .get_default(&mut store, "")?
986 .typed::<(), ()>(&store)?
987 .call(&mut store, ())
988 .is_err()
989 {
990 return Err("wasm call failed".into());
991 }
992
993 let out = out.lock();
994 Ok(out.clone())
995 }
996}