Skip to main content

psibase/services/
sites.rs

1#![allow(non_snake_case)]
2use crate::AccountNumber;
3
4#[allow(dead_code)]
5type SitesContentKey = (AccountNumber, String);
6/// Decompress content
7///
8/// `DecompressorInterface` is implemented by services that can decompress content
9/// with a specific encoding.
10///
11/// Decompressor services should not inherit from this struct,
12/// instead they should define actions with matching signatures.
13///
14/// The `sites` service uses decompressor services who implement this interface to
15/// decompress content when the client's accepted encodings do not include the
16/// content's encoding.
17#[allow(dead_code)]
18struct DecompressorInterface;
19impl DecompressorInterface {
20    /// Decompresses content compressed with some algorithm
21    #[allow(dead_code, unused_variables)]
22    fn decompress(content: Vec<u8>) -> Vec<u8> {
23        unimplemented!()
24    }
25}
26
27#[crate::service(name = "sites", dispatch = false, psibase_mod = "crate")]
28#[allow(non_snake_case, unused_variables)]
29pub mod service {
30    use crate::fracpack::{Pack, ToSchema, Unpack};
31    use crate::{http::HttpRequest, AccountNumber, Checksum256, Hex};
32    use async_graphql::SimpleObject;
33    use serde::{Deserialize, Serialize};
34
35    #[table(name = "SitesContentTable", index = 0)]
36    #[derive(Debug, Clone, Serialize, Deserialize, SimpleObject, ToSchema, Pack, Unpack)]
37    #[fracpack(fracpack_mod = "fracpack")]
38    pub struct SitesContentRow {
39        pub account: AccountNumber,
40        pub path: String,
41        pub contentType: String,
42        pub contentHash: Checksum256,
43        pub contentEncoding: Option<String>,
44        pub csp: Option<String>,
45    }
46
47    impl SitesContentRow {
48        #[primary_key]
49        fn pk(&self) -> (AccountNumber, String) {
50            (self.account, self.path.clone())
51        }
52    }
53
54    #[table(name = "SiteConfigTable", index = 1)]
55    #[derive(Debug, Clone, Serialize, Deserialize, SimpleObject, ToSchema, Pack, Unpack)]
56    #[fracpack(fracpack_mod = "fracpack")]
57    pub struct SiteConfigRow {
58        #[primary_key]
59        pub account: AccountNumber,
60        pub spa: bool,
61        pub cache: bool,
62        pub globalCsp: Option<String>,
63        pub proxyAccount: Option<AccountNumber>,
64    }
65
66    #[table(name = "SitesDataTable", index = 2)]
67    #[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Pack, Unpack)]
68    #[fracpack(fracpack_mod = "fracpack")]
69    pub struct SitesDataRow {
70        #[primary_key]
71        pub hash: Checksum256,
72        pub data: Vec<u8>,
73    }
74
75    #[table(name = "SitesDataRefTable", index = 3)]
76    #[derive(Debug, Clone, Serialize, Deserialize, ToSchema, Pack, Unpack)]
77    #[fracpack(fracpack_mod = "fracpack")]
78    pub struct SitesDataRefRow {
79        #[primary_key]
80        pub hash: Checksum256,
81        pub refs: u32,
82    }
83
84    /// Serves a request by looking up the content uploaded to the specified subdomain
85    #[action]
86    fn serveSys(request: HttpRequest, socket: Option<i32>) -> Option<crate::http::HttpReply> {
87        unimplemented!()
88    }
89
90    /// Stores content accessible at the caller's subdomain
91    #[action]
92    fn storeSys(
93        path: String,
94        contentType: String,
95        contentEncoding: Option<String>,
96        content: Hex<Vec<u8>>,
97    ) {
98        unimplemented!()
99    }
100
101    /// Stores content accessible at the caller's subdomain
102    #[action]
103    fn hardlink(
104        path: String,
105        contentType: String,
106        contentEncoding: Option<String>,
107        contentHash: Checksum256,
108    ) {
109        unimplemented!()
110    }
111
112    /// Removes content from the caller's subdomain
113    #[action]
114    fn remove(path: String) {
115        unimplemented!()
116    }
117
118    /// Checks whether a request for content on a site at the given path is valid (such a request will not produce a 404).
119    ///
120    /// Note: For single-page applications, static assets (e.g. 'style.css') can be checked normally. However, all other assets
121    /// are routed client-side, so a route like `/page1` is considered a valid route as long as the SPA serves a root document.
122    #[action]
123    fn isValidPath(site: AccountNumber, path: String) -> bool {
124        unimplemented!()
125    }
126
127    /// Get file properties for a given site and path
128    #[action]
129    fn getProps(site: AccountNumber, path: String) -> Option<SitesContentRow> {
130        unimplemented!()
131    }
132
133    /// Get raw file data from a site, optionally decompressing it.
134    ///
135    /// Aborts if the file is not found.
136    ///
137    /// If the file has no encoding, or decompression is not requested, the raw data is returned.
138    /// Otherwise, the data is decompressed. This action aborts if no decompressor is available
139    /// for the file's encoding.
140    #[action]
141    fn getData(site: AccountNumber, path: String, decompress: bool) -> Vec<u8> {
142        unimplemented!()
143    }
144
145    /// Enables/disables single-page application mode.
146    /// When enabled, all content requests return the root document.
147    #[action]
148    fn enableSpa(enable: bool) {
149        unimplemented!()
150    }
151
152    /// Sets the Content Security Policy for the specified path (or "*" for a global CSP).
153    /// If a specific CSP is set, it takes precedence over the global CSP.
154    /// If no specific or global CSP is set, a default CSP is used.
155    ///
156    /// The CSP string may include the keyword `{{root}}`, which is replaced
157    /// with the root domain (including port when present),
158    /// e.g. `psibase.localhost:8080` or `example.com`. Use this for
159    /// subdomain-scoped sources such as `connect-src 'self' {{root}} *.{{root}}`
160    /// so the same policy works wherever the app is served, without
161    /// hardcoding hosts.
162    #[action]
163    fn setCsp(path: String, csp: String) {
164        unimplemented!()
165    }
166
167    /// Deletes the Content Security Policy for the specified path (or "*" for the global CSP).
168    #[action]
169    fn deleteCsp(path: String) {
170        unimplemented!()
171    }
172
173    /// Enables/disables caching of responses (Enabled by default)
174    /// Cache strategy:
175    /// - `If-None-Match` header is checked against the hash of the content
176    /// - The hash is stored in the `ETag` header
177    /// - If the hash matches, a 304 Not Modified response is returned
178    /// - If the hash does not match, the new content is returned with an updated `ETag` header
179    #[action]
180    fn enableCache(enable: bool) {
181        unimplemented!()
182    }
183
184    /// When `serveSys` looks up a path on the caller's site, and no file matches (after SPA and
185    /// index rules), it tries the same path on `proxy`'s uploaded content, then on that site's
186    /// proxy if configured, and so on.
187    ///
188    /// Files that exist on the caller's site always take precedence.
189    ///
190    ///  A proxy chain must not contain a cycle or `setProxy` aborts.
191    #[action]
192    fn setProxy(proxy: AccountNumber) {
193        unimplemented!()
194    }
195
196    /// Removes the proxy fallback for the caller's site. No-op if none is set.
197    #[action]
198    fn clearProxy() {
199        unimplemented!()
200    }
201}
202
203#[test]
204fn verify_schema() {
205    crate::assert_schema_matches_package::<Wrapper>();
206}