Skip to main content

systemprompt_config/paths/
storage.rs

1//! Storage path layout for uploaded and generated files.
2//!
3//! `data` is the one sanctioned root for runtime state the server writes for
4//! itself (the gateway accounting journal), and `data/scratch` the one root
5//! for short-lived working trees (Git checkouts of managed sources, import
6//! staging), so a read-only root filesystem needs no writable `/tmp`. Both
7//! sit under `paths.storage` because that is the mount every deployment
8//! already makes writable; the profile directory is configuration and may be
9//! mounted read-only.
10//!
11//! Copyright (c) systemprompt.io — Business Source License 1.1.
12//! See <https://systemprompt.io> for licensing details.
13
14use std::path::{Path, PathBuf};
15
16use super::PathError;
17use systemprompt_manifest::paths::constants::storage;
18use systemprompt_manifest::profile::PathsConfig;
19
20#[derive(Debug, Clone)]
21pub struct StoragePaths {
22    root: PathBuf,
23    files: PathBuf,
24    exports: PathBuf,
25    data: PathBuf,
26    scratch: PathBuf,
27    css: PathBuf,
28    js: PathBuf,
29    fonts: PathBuf,
30    images: PathBuf,
31    generated_images: PathBuf,
32    logos: PathBuf,
33    audio: PathBuf,
34    video: PathBuf,
35    documents: PathBuf,
36    uploads: PathBuf,
37}
38
39impl StoragePaths {
40    pub fn from_profile(paths: &PathsConfig) -> Result<Self, PathError> {
41        let root = paths
42            .storage
43            .as_ref()
44            .ok_or(PathError::NotConfigured { field: "storage" })?;
45
46        let root = PathBuf::from(root);
47        let files = root.join(storage::FILES);
48
49        Ok(Self {
50            exports: root.join(storage::EXPORTS),
51            data: root.join(storage::DATA),
52            scratch: root.join(storage::SCRATCH),
53            css: files.join("css"),
54            js: files.join("js"),
55            fonts: files.join("fonts"),
56            images: files.join("images"),
57            generated_images: files.join("images/generated"),
58            logos: files.join("images/logos"),
59            audio: files.join("audio"),
60            video: files.join("video"),
61            documents: files.join("documents"),
62            uploads: files.join("uploads"),
63            files,
64            root,
65        })
66    }
67
68    pub fn root(&self) -> &Path {
69        &self.root
70    }
71
72    pub fn files(&self) -> &Path {
73        &self.files
74    }
75
76    pub fn exports(&self) -> &Path {
77        &self.exports
78    }
79
80    pub fn data(&self) -> &Path {
81        &self.data
82    }
83
84    pub fn scratch(&self) -> &Path {
85        &self.scratch
86    }
87
88    pub fn css(&self) -> &Path {
89        &self.css
90    }
91
92    pub fn js(&self) -> &Path {
93        &self.js
94    }
95
96    pub fn fonts(&self) -> &Path {
97        &self.fonts
98    }
99
100    pub fn images(&self) -> &Path {
101        &self.images
102    }
103
104    pub fn generated_images(&self) -> &Path {
105        &self.generated_images
106    }
107
108    pub fn logos(&self) -> &Path {
109        &self.logos
110    }
111
112    pub fn audio(&self) -> &Path {
113        &self.audio
114    }
115
116    pub fn video(&self) -> &Path {
117        &self.video
118    }
119
120    pub fn documents(&self) -> &Path {
121        &self.documents
122    }
123
124    pub fn uploads(&self) -> &Path {
125        &self.uploads
126    }
127}