Compare commits
	
		
			2 Commits
		
	
	
		
			setup-init
			...
			docs-forma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 57610d69dc | |||
| 39868563af | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -16,5 +16,3 @@ target/ | ||||
|  | ||||
| # No need to store databases in the git repo | ||||
| **/*.sqlite | ||||
| .env | ||||
| aural_isle.sqlite.db | ||||
|  | ||||
							
								
								
									
										30
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| # Contribution Guidelines | ||||
|  | ||||
| ## Code Formatting | ||||
|  | ||||
| We format all Rust code using edition 2021 rules. This edition is defined in our Cargo.toml and most methods below will pick this up automatically. | ||||
|  | ||||
| ### Manual Method | ||||
|  | ||||
| The most editor agnostic way to format your Rust code is by manually running cargo's fmt sub command. | ||||
|  | ||||
| To check if your code is formatted without making changes, run `cargo fmt --check`. Then if you want to format your code you can run just `cargo fmt`. | ||||
|  | ||||
| ### VScode | ||||
|  | ||||
| We recommend the meta extension Rust, which includes rust-analyzer. Then to set your editor to format on save: | ||||
|  | ||||
| * In Visual Studio Code, press Control + Shift + P or Command + Shift + P (Mac) to open the command palette and type setting and then select Preferences: Open User Settings option. | ||||
| * Search for format on save setting and check the checkbox. | ||||
|  | ||||
| You can also set language specific settings as outlined [here](https://code.visualstudio.com/docs/getstarted/settings#_language-specific-editor-settings). You will again want to then search for format on save and check the checkbox. | ||||
|  | ||||
| ### Other Editors | ||||
|  | ||||
| You will first need to [install rust-analyzer](https://rust-analyzer.github.io/manual.html#installation), then configure your editor and install any needed plugins or extensions as outlined on that page. | ||||
|  | ||||
| ### Pre Commit Hook | ||||
|  | ||||
| If you want to think about formatting your code less and just have git yell at you whe you try to commit code that is not properly formatted, you can use a simple pre-commit hook. | ||||
|  | ||||
| After cloning the repo, the file `pre-commit` to `.git/hooks/pre-commit`. That's it. | ||||
							
								
								
									
										935
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										935
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -4,11 +4,8 @@ version = "0.1.0" | ||||
| edition = "2021" | ||||
|  | ||||
| [dependencies] | ||||
| sqlx = { version = "0.7", features = [ "runtime-tokio" ] } | ||||
| tokio = { version = "1.2", features = ["full"] } | ||||
| warp = "0.3" | ||||
| rusty-money = "0.4.1" | ||||
| dotenv = "0.15.0" | ||||
| serde = { version = "1.0", features = ["derive"] } | ||||
| serde_json = "1.0" | ||||
|  | ||||
|  | ||||
							
								
								
									
										11
									
								
								DEVELOP.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								DEVELOP.md
									
									
									
									
									
								
							| @ -1,11 +0,0 @@ | ||||
| # Dev Notes | ||||
|  | ||||
| To develop against this project, you will need Rust 1.70 or greater installed. | ||||
|  | ||||
| You will want to install the `sqlx-cli` tool via Cargo: `cargo install sqlx-cli` | ||||
|  | ||||
| You will need to initialize a sqlite database store: `touch aural_isle.sqlite.db` | ||||
|  | ||||
| You will need to copy the example env file to .env: `cp env.example .env` | ||||
|  | ||||
| Then you should be able to run the initial database migrations: `sqlx migrate run`. (During initial dev, if you have already run the initial migration, you will need to delete the sqlite database store and re-create it as above) | ||||
							
								
								
									
										108
									
								
								MODELS.md
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								MODELS.md
									
									
									
									
									
								
							| @ -185,6 +185,7 @@ struct Track { | ||||
|     is_available: bool, | ||||
|     preview_source: Option<String>, | ||||
|     source: String, | ||||
|     price: Option<rusty_money::Money>, | ||||
|     created_by: PersonId, | ||||
|     created_at: chrono::DateTime, | ||||
|     modified_by: PersonId, | ||||
| @ -217,6 +218,7 @@ struct Album { | ||||
|     is_available: bool, | ||||
|     preview_source: Option<String>, | ||||
|     source: String, | ||||
|     price: Option<rusty_money::Money>, | ||||
|     created_by: PersonId, | ||||
|     created_at: chrono::DateTime, | ||||
|     modified_by: PersonId, | ||||
| @ -287,14 +289,23 @@ struct Article { | ||||
| A Comment represents a textual comment that a Person wants to attach contextually to an Artist, an Album, a Track, or a Playlist. | ||||
|  | ||||
| ``` rust | ||||
| enum CommentTarget { | ||||
|     Label(LabelId), | ||||
|     Artist(ArtistId), | ||||
|     Album(AlbumId), | ||||
|     Track(TrackId), | ||||
|     Playlist(PlaylistId), | ||||
|     Article(ArticleId), | ||||
|     Comment(CommentId), | ||||
| enum CommentTargetType { | ||||
|     Label, | ||||
|     Artist, | ||||
|     Album, | ||||
|     Track, | ||||
|     Playlist, | ||||
|     Article, | ||||
|     Comment, | ||||
| } | ||||
| struct CommentTargetId { | ||||
|     label_id: Option<LabelId>, | ||||
|     artist_id: Option<ArtistId>, | ||||
|     album_id: Option<AlbumId>, | ||||
|     track_id: Option<TrackId>, | ||||
|     playlist_id: Option<PlaylistId>, | ||||
|     article_id: Option<ArticleId>, | ||||
|     comment_id: Option<CommentId>, | ||||
| } | ||||
| struct CommentId { | ||||
|     id: String, | ||||
| @ -302,7 +313,8 @@ struct CommentId { | ||||
| struct Comment { | ||||
|     id: CommentId, | ||||
|     body: String, | ||||
|     target: CommentTarget, | ||||
|     target_type: CommentTargetType, | ||||
|     target_id: CommentTargetId, | ||||
|     created_by: PersonId, | ||||
|     created_at: chrono::DateTime, | ||||
|     modified_by: Option<PersonId>, | ||||
| @ -372,14 +384,9 @@ struct Video { | ||||
|  | ||||
| # Collection | ||||
|  | ||||
| A Collection represents one or more Albums, Tracks, and/or OtherProducts that are offered together as a package for Purchase | ||||
| A Collection represents one or more Albums and/or Tracks that are offered together as a package for Purchase | ||||
|  | ||||
| ``` rust | ||||
| enum CollectionItem { | ||||
|     Album(AlbumId), | ||||
|     Track(TrackId), | ||||
|     OtherProduct(OtherProductId), | ||||
| } | ||||
| struct CollectionId { | ||||
|     id: String, | ||||
| } | ||||
| @ -395,7 +402,10 @@ struct Collection { | ||||
|     is_available: bool, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     deleted_at: Option<chrono::DateTime>, | ||||
|     collection_items: Option<Vec<CollectionMember>>, | ||||
|     albums: Option<Vec<Album>>, | ||||
|     tracks: Option<Vec<Track>>, | ||||
|     other_products: Option<Vec<OtherProduct>>, | ||||
|     price: Option<rusty_money::Money>, | ||||
| } | ||||
| ``` | ||||
|  | ||||
| @ -419,20 +429,20 @@ struct OtherProduct { | ||||
|     is_available: bool, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     deleted_at: Option<chrono::DateTime>, | ||||
|     requires_shipping: bool, | ||||
|     price: Option<rusty_money::Money> | ||||
| } | ||||
| ``` | ||||
|  | ||||
| # Purchase | ||||
|  | ||||
| A Purchase represents one or more Tracks, Albums, Collections, and/or OtherProducts that a Person purchases from an Artist or Label through the server. | ||||
| A Purchase represents one or more Tracks or Albums that a Person purchases from an Artist or Label through the server. | ||||
|  | ||||
| ``` rust | ||||
| enum PurchaseItemType { | ||||
|     Album(AlbumId), | ||||
|     Track(TrackId), | ||||
|     Collection(CollectionId), | ||||
|     OtherProduct(OtherProductId), | ||||
|     Album, | ||||
|     Track, | ||||
|     Collection, | ||||
|     OtherProduct, | ||||
| } | ||||
| struct PurchaseItemId { | ||||
|     id: String, | ||||
| @ -442,16 +452,18 @@ struct SkuId { | ||||
| } | ||||
| struct Sku { | ||||
|     id: SkuId, | ||||
|     item: PurchaseItemType, | ||||
|     album_id: Option<AlbumId>, | ||||
|     track_id: Option<TrackId>, | ||||
|     collection_id: Option<CollectionId>, | ||||
|     other_product_id: Option<OtherProductId>, | ||||
|     variant: Option<String>, | ||||
|     description: Option<String>, | ||||
|     price: rusty_money::Money, | ||||
|     requires_shipping: bool, | ||||
|     shipping_charge: Option<rusty_money::Money>, | ||||
|     discount_percentage: Option<f64>, | ||||
|     discount_flatrate: Option<rusty_money::Money>, | ||||
| } | ||||
| struct LineItem { | ||||
|     id: LineItemId, | ||||
|     sku: SkuId, | ||||
| struct PurchaseItem { | ||||
|     id: PurchaseItemId, | ||||
|     type: PurchaseItemType, | ||||
|     sku: Option<SkuId>, | ||||
|     quantity: usize, | ||||
| } | ||||
| enum PurchaseState { | ||||
| @ -465,41 +477,9 @@ enum PurchaseState { | ||||
| } | ||||
| struct Purchase { | ||||
|     id: String, | ||||
|     items: Vec<LineItem>, | ||||
|     items: Vec<PurchaseItem>, | ||||
|     state: PurchaseState, | ||||
|     purchased_by: Option<Person>, | ||||
|     purchased_by: Person, | ||||
|     purchased_at: Option<chrono::DateTime>, | ||||
|     fulfilled_by: Option<Person>, | ||||
|     fulfilled_at: Option<chrono::DateTime>, | ||||
|     coupons_applied: Option<Vec<CouponCodeId>>, | ||||
|     total_charge: rusty_money::Money, | ||||
| } | ||||
| ``` | ||||
|  | ||||
| # CouponCode | ||||
|  | ||||
| A CouponCode represents a code a Person can enter to receive a discount on a PurchateItem or on a Purchase. | ||||
|  | ||||
| ``` rust | ||||
| struct CouponCodeId { | ||||
|     id: String, | ||||
| } | ||||
| struct CouponCode { | ||||
|     id: CouponCodeId, | ||||
|     name: String, | ||||
|     code: String, | ||||
|     uses: usize, | ||||
|     max_uses: usize, | ||||
|     expiration: Option<chrono::DateTime>, | ||||
|     discount_flat: Option<rusty_money::Money>, | ||||
|     discount_percentage: Option<f32>, | ||||
|     skus: Option<Vec<SkuId>>, | ||||
|     is_active: bool, | ||||
|     created_by: PersonId, | ||||
|     created_at: chrono::DateTime, | ||||
|     modified_by: PersonId, | ||||
|     modified_at: chrono::DateTime, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     deleted_at: Option<chrono::DateTime>, | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										284
									
								
								SCHEMA.md
									
									
									
									
									
								
							
							
						
						
									
										284
									
								
								SCHEMA.md
									
									
									
									
									
								
							| @ -24,6 +24,7 @@ CREATE TABLE Persons ( | ||||
| 	deleted_at INTEGER,               /* timestamp */ | ||||
| 	deleted_by TEXT(36),              /* UUIDv4 */ | ||||
| 	last_seen INTEGER,                /* timestamp */ | ||||
| 	shipping_address TEXT,            /* optional, should use \n between lines to keep localized format as needed */ | ||||
| 	CONSTRAINT Persons_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Persons_FK FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Persons_FK_1 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| @ -36,28 +37,6 @@ CREATE INDEX Persons_is_blocked_IDX ON Persons (is_blocked); | ||||
| CREATE INDEX Persons_deleted_at_IDX ON Persons (deleted_at); | ||||
| ``` | ||||
|  | ||||
| # PersonShippingAddresses | ||||
| The PersonShippingAddresses table will contain zero or more shipping addresses as used by a Person | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE IF NOT EXISTS PersonShippingAddresses ( | ||||
|     id TEXT(36),                        /* UUIDv4 */ | ||||
|     person_id TEXT(36),                 /* UUIDv4 */  | ||||
|     shipping_address TEXT,              /* mandatory, should use \n between lines to keep localized format as needed */  | ||||
|     is_primary INTEGER DEFAULT (0),     /* bool, default false */  | ||||
|     created_at INTEGER,                 /* timestamp */  | ||||
|     modified_at INTEGER,                /* timestamp */  | ||||
|     modified_by TEXT(36),               /* UUIDv4 */  | ||||
|     deleted_at INTEGER,                 /* timestamp */  | ||||
|     deleted_by TEXT(36),                /* UUIDv4 */  | ||||
|     CONSTRAINT PersonShippingAddresses_PK PRIMARY KEY (id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK FOREIGN KEY (person_id) REFERENCES Persons(id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS PersonShippingAddresses_is_primary_IDX ON PersonShippingAddresses (is_primary); | ||||
| ``` | ||||
|  | ||||
| # PersonEmails Table | ||||
| The PersonEmails table is a one-to-many lookup table relating a Person to zero or more email addresses | ||||
|  | ||||
| @ -212,7 +191,6 @@ The Artists table will contain Artists! | ||||
| ``` sql | ||||
| CREATE TABLE Artists ( | ||||
| 	id TEXT(36), | ||||
| 	label_id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	bio TEXT, | ||||
| 	website TEXT, | ||||
| @ -727,262 +705,26 @@ CREATE INDEX ArticleComments_article_id_IDX ON ArticleComments (article_id); | ||||
| CREATE INDEX ArticleComments_comment_id_IDX ON ArticleComments (comment_id); | ||||
| ``` | ||||
|  | ||||
| # OtherProducts | ||||
| The OtherProducts table will contain products for sale that are not Albums nor Tracks nor Collections. | ||||
| # LabelImages | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE OtherProducts ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	description TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (0), | ||||
| 	is_available INTEGER DEFAULT (0), | ||||
| 	requires_shipping INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT OtherProducts_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT OtherProducts_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT OtherProducts_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT OtherProducts_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX OtherProducts_name_IDX ON OtherProducts (name); | ||||
| CREATE INDEX OtherProducts_description_IDX ON OtherProducts (description); | ||||
| CREATE INDEX OtherProducts_created_by_IDX ON OtherProducts (created_by); | ||||
| CREATE INDEX OtherProducts_created_at_IDX ON OtherProducts (created_at); | ||||
| CREATE INDEX OtherProducts_modified_by_IDX ON OtherProducts (modified_by); | ||||
| CREATE INDEX OtherProducts_modified_at_IDX ON OtherProducts (modified_at); | ||||
| CREATE INDEX OtherProducts_deleted_by_IDX ON OtherProducts (deleted_by); | ||||
| CREATE INDEX OtherProducts_deleted_at_IDX ON OtherProducts (deleted_at); | ||||
| CREATE INDEX OtherProducts_is_public_IDX ON OtherProducts (is_public); | ||||
| CREATE INDEX OtherProducts_is_available_IDX ON OtherProducts (is_available); | ||||
| CREATE INDEX OtherProducts_requires_shipping_IDX ON OtherProducts (requires_shipping); | ||||
| ``` | ||||
| # LabelVideos | ||||
|  | ||||
| # Collections | ||||
| The Collections table will contain Collections where each Collection is comprised of one or more Tracks, Albums, and/or OtherProducts packaged together for sale. | ||||
| # ArtistImages | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE Collections ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	description TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (0), | ||||
| 	is_available INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT Collections_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Collections_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Collections_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Collections_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX Collections_name_IDX ON Collections (name); | ||||
| CREATE INDEX Collections_description_IDX ON Collections (description); | ||||
| CREATE INDEX Collections_created_by_IDX ON Collections (created_by); | ||||
| CREATE INDEX Collections_created_at_IDX ON Collections (created_at); | ||||
| CREATE INDEX Collections_modified_by_IDX ON Collections (modified_by); | ||||
| CREATE INDEX Collections_modified_at_IDX ON Collections (modified_at); | ||||
| CREATE INDEX Collections_deleted_by_IDX ON Collections (deleted_by); | ||||
| CREATE INDEX Collections_deleted_at_IDX ON Collections (deleted_at); | ||||
| CREATE INDEX Collections_is_public_IDX ON Collections (is_public); | ||||
| CREATE INDEX Collections_is_available_IDX ON Collections (is_available); | ||||
| ``` | ||||
| # ArtistVideos | ||||
|  | ||||
| # CollectionAlbums | ||||
| The CollectionAlbums table will relate a Collection to the Album(s) it contains. | ||||
| # AlbumImages | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE CollectionAlbums ( | ||||
| 	collection_id TEXT(36), | ||||
| 	album_id TEXT(36), | ||||
| 	CONSTRAINT CollectionAlbums_PK PRIMARY KEY (collection_id,album_id), | ||||
| 	CONSTRAINT CollectionAlbums_FK FOREIGN KEY (collection_id) REFERENCES Collections(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT CollectionAlbums_FK_1 FOREIGN KEY (album_id) REFERENCES Albums(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX CollectionAlbums_collection_id_IDX ON CollectionAlbums (collection_id); | ||||
| CREATE INDEX CollectionAlbums_album_id_IDX ON CollectionAlbums (album_id); | ||||
| ``` | ||||
| # AlbumVideos | ||||
|  | ||||
| # CollectionTracks | ||||
| The CollectionTracks table will relate a Collection to the Track(s) it contains. | ||||
| # TrackImages | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE CollectionTracks ( | ||||
| 	collection_id TEXT(36), | ||||
| 	track_id TEXT(36), | ||||
| 	CONSTRAINT CollectionTracks_PK PRIMARY KEY (collection_id,track_id), | ||||
| 	CONSTRAINT CollectionTracks_FK FOREIGN KEY (collection_id) REFERENCES Collections(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT CollectionTracks_FK_1 FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX CollectionTracks_collection_id_IDX ON CollectionTracks (collection_id); | ||||
| CREATE INDEX CollectionTracks_track_id_IDX ON CollectionTracks (track_id); | ||||
| ``` | ||||
| # TrackVideos | ||||
|  | ||||
| # CollectionOtherProducts | ||||
| The CollectionOtherProducts table will relate a Collection to the OtherProduct(s) it contains. | ||||
| # PlaylistImages | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE CollectionOtherProducts ( | ||||
| 	collection_id TEXT(36), | ||||
| 	other_product_id TEXT(36), | ||||
| 	CONSTRAINT CollectionOtherProducts_PK PRIMARY KEY (collection_id,other_product_id), | ||||
| 	CONSTRAINT CollectionOtherProducts_FK FOREIGN KEY (collection_id) REFERENCES Collections(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT CollectionOtherProducts_FK_1 FOREIGN KEY (other_product_id) REFERENCES OtherProducts(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX CollectionOtherProducts_collection_id_IDX ON CollectionOtherProducts (collection_id); | ||||
| CREATE INDEX CollectionOtherProducts_other_product_id_IDX ON CollectionOtherProducts (other_product_id); | ||||
| ``` | ||||
| # PlaylistVideos | ||||
|  | ||||
| # Skus | ||||
| The Skus table will contain SKUs () representing items available for Purchase through the server. | ||||
| # ArticleImages | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE Skus ( | ||||
| 	id TEXT(36), | ||||
| 	item_type TEXT, | ||||
| 	item_id TEXT(36), | ||||
| 	variant TEXT, | ||||
| 	description TEXT, | ||||
| 	price NUMERIC DEFAULT (0.00), | ||||
| 	requires_shipping INTEGER DEFAULT (0), | ||||
| 	shipping_charge NUMERIC DEFAULT (0.00), | ||||
| 	CONSTRAINT Skus_PK PRIMARY KEY (id) | ||||
| ); | ||||
| CREATE INDEX Skus_item_type_IDX ON Skus (item_type); | ||||
| CREATE INDEX Skus_item_id_IDX ON Skus (item_id); | ||||
| CREATE INDEX Skus_variant_IDX ON Skus (variant); | ||||
| CREATE INDEX Skus_description_IDX ON Skus (description); | ||||
| CREATE INDEX Skus_price_IDX ON Skus (price); | ||||
| CREATE INDEX Skus_requires_shipping_IDX ON Skus (requires_shipping); | ||||
| CREATE INDEX Skus_shipping_charge_IDX ON Skus (shipping_charge); | ||||
| ``` | ||||
|  | ||||
| # LineItems | ||||
| The LineItems table will contain individual SKUs and their associated quantites as part of a Purchase. | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE LineItems ( | ||||
| 	id TEXT(36), | ||||
| 	sku_id TEXT(36), | ||||
| 	quantity INTEGER DEFAULT (1), | ||||
| 	CONSTRAINT LineItems_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT LineItems_FK FOREIGN KEY (sku_id) REFERENCES Skus(id) | ||||
| ); | ||||
| CREATE INDEX LineItems_sku_id_IDX ON LineItems (sku_id); | ||||
| CREATE INDEX LineItems_quantity_IDX ON LineItems (quantity); | ||||
| ``` | ||||
|  | ||||
| # CouponCodes | ||||
| The CouponCodes table will contain coupon codes that can be redeemed for a discount, either by amount or percentage, on a Purchase. | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE CouponCodes ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	code TEXT, | ||||
| 	uses INTEGER DEFAULT (0), | ||||
| 	max_uses INTEGER DEFAULT (0), | ||||
| 	expiration INTEGER, | ||||
| 	discount_flat NUMERIC DEFAULT (0.00), | ||||
| 	discount_percentage NUMERIC DEFAULT (0.00), | ||||
| 	is_sku_specific INTEGER DEFAULT (0), | ||||
| 	is_active INTEGER DEFAULT (0), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	CONSTRAINT CouponCodes_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT CouponCodes_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT CouponCodes_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT CouponCodes_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX CouponCodes_name_IDX ON CouponCodes (name); | ||||
| CREATE UNIQUE INDEX CouponCodes_code_IDX ON CouponCodes (code); | ||||
| CREATE INDEX CouponCodes_uses_IDX ON CouponCodes (uses); | ||||
| CREATE INDEX CouponCodes_max_uses_IDX ON CouponCodes (max_uses); | ||||
| CREATE INDEX CouponCodes_expiration_IDX ON CouponCodes (expiration); | ||||
| CREATE INDEX CouponCodes_is_sku_specific_IDX ON CouponCodes (is_sku_specific); | ||||
| CREATE INDEX CouponCodes_is_active_IDX ON CouponCodes (is_active); | ||||
| CREATE INDEX CouponCodes_created_by_IDX ON CouponCodes (created_by); | ||||
| CREATE INDEX CouponCodes_created_at_IDX ON CouponCodes (created_at); | ||||
| CREATE INDEX CouponCodes_modified_by_IDX ON CouponCodes (modified_by); | ||||
| CREATE INDEX CouponCodes_modified_at_IDX ON CouponCodes (modified_at); | ||||
| CREATE INDEX CouponCodes_deleted_by_IDX ON CouponCodes (deleted_by); | ||||
| CREATE INDEX CouponCodes_deleted_at_IDX ON CouponCodes (deleted_at); | ||||
| ``` | ||||
|  | ||||
| # CouponCodeSkus | ||||
| The CouponCodeSkus table will relate a CouponCode to one or more Skus the CouponCode can be applied to, if CouponCode.is_sku_specific is true. (If CouponCode.is_sku_specific is false the CouponCode can be applied to an entire Purchase regardless of the Skus being purchased.) | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE CouponCodeSkus ( | ||||
| 	coupon_code_id TEXT(36), | ||||
| 	sku_id TEXT(36), | ||||
| 	CONSTRAINT CouponCodeSkus_PK PRIMARY KEY (coupon_code_id,sku_id), | ||||
| 	CONSTRAINT CouponCodeSkus_FK FOREIGN KEY (coupon_code_id) REFERENCES CouponCodes(id), | ||||
| 	CONSTRAINT CouponCodeSkus_FK_1 FOREIGN KEY (sku_id) REFERENCES Skus(id) | ||||
| ); | ||||
| ``` | ||||
|  | ||||
| # Purchases | ||||
| The Purchases table will contain Purchases! | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE Purchases ( | ||||
| 	id TEXT(36), | ||||
| 	state TEXT, | ||||
| 	purchased_by TEXT(36), | ||||
| 	purchased_at INTEGER, | ||||
| 	fulfilled_by TEXT(36), | ||||
| 	fulfilled_at INTEGER, | ||||
| 	total_charge NUMERIC DEFAULT (0.00), | ||||
| 	CONSTRAINT Purchases_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Purchases_FK FOREIGN KEY (purchased_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Purchases_FK_1 FOREIGN KEY (fulfilled_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX Purchases_state_IDX ON Purchases (state); | ||||
| CREATE INDEX Purchases_purchased_by_IDX ON Purchases (purchased_by); | ||||
| CREATE INDEX Purchases_purchased_at_IDX ON Purchases (purchased_at); | ||||
| CREATE INDEX Purchases_fulfilled_by_IDX ON Purchases (fulfilled_by); | ||||
| CREATE INDEX Purchases_fulfilled_at_IDX ON Purchases (fulfilled_at); | ||||
| CREATE INDEX Purchases_total_charge_IDX ON Purchases (total_charge); | ||||
| ``` | ||||
|  | ||||
| # PurchaseLineItems | ||||
| The PurchaseLineItems table will relate a Purchase to one or more LineItems. | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE PurchaseLineItems ( | ||||
| 	purchase_id TEXT(36), | ||||
| 	line_item_id TEXT(36), | ||||
| 	CONSTRAINT PurchaseLineItems_PK PRIMARY KEY (purchase_id,line_item_id), | ||||
| 	CONSTRAINT PurchaseLineItems_FK FOREIGN KEY (purchase_id) REFERENCES Purchases(id), | ||||
| 	CONSTRAINT PurchaseLineItems_FK_1 FOREIGN KEY (line_item_id) REFERENCES LineItems(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX PurchaseLineItems_purchase_id_IDX ON PurchaseLineItems (purchase_id); | ||||
| CREATE INDEX PurchaseLineItems_line_item_id_IDX ON PurchaseLineItems (line_item_id); | ||||
| ``` | ||||
|  | ||||
| # PurchaseCouponCodes | ||||
| The PurchaseCouponCodes table will relate a Purchase to one or more CouponCodes that were successfully applied to the Purchase. | ||||
|  | ||||
| ``` sql | ||||
| CREATE TABLE PurchaseCouponCodes ( | ||||
| 	purchase_id TEXT(36), | ||||
| 	coupon_code_id TEXT(36), | ||||
| 	CONSTRAINT PurchaseCouponCodes_PK PRIMARY KEY (purchase_id,coupon_code_id), | ||||
| 	CONSTRAINT PurchaseCouponCodes_FK FOREIGN KEY (purchase_id) REFERENCES Purchases(id), | ||||
| 	CONSTRAINT PurchaseCouponCodes_FK_1 FOREIGN KEY (coupon_code_id) REFERENCES CouponCodes(id) | ||||
| ); | ||||
| CREATE INDEX PurchaseCouponCodes_purchase_id_IDX ON PurchaseCouponCodes (purchase_id); | ||||
| CREATE INDEX PurchaseCouponCodes_coupon_code_id_IDX ON PurchaseCouponCodes (coupon_code_id); | ||||
| ``` | ||||
| # ArticleVideos | ||||
| @ -7,8 +7,8 @@ | ||||
| #   listener - configure instance for a single Person who is focused on listening (a fan, a music collector, someone who wants to share their library with others) | ||||
| INSTALLATION_MODE=multilabel | ||||
|  | ||||
| # DATABASE_URL represents the connection string for establishing a connection to the data store for this instance  | ||||
| DATABASE_URL=sqlite://aural_isle.sqlite.db  | ||||
| # DATABASE_URI represents the connection string for establishing a connection to the data store for this instance  | ||||
| DATABASE_URI=sqlite::memory: | ||||
|  | ||||
| # FILESTORE takes one of the following values: | ||||
| #   local - use local filesystem for storage | ||||
| @ -16,7 +16,7 @@ DATABASE_URL=sqlite://aural_isle.sqlite.db | ||||
| FILESTORE=local | ||||
|  | ||||
| # S3_URI represents the connection string for establishing a connection to the storage system if FILESTORE=s3  | ||||
| #S3_URI= | ||||
| S3_URI= | ||||
|  | ||||
| # SERVER_ADMIN_NAME is the name to display as the technical/administrative contact for this instance | ||||
| SERVER_ADMIN_NAME=DJ Sundog | ||||
|  | ||||
| @ -1,803 +0,0 @@ | ||||
| -- Initial SQLITE schema for Aural Isle | ||||
|  | ||||
| --This file represents the initial schema for the various data structures to be stored in the database. | ||||
|  | ||||
| -- The Persons table will contain data about Person entities (see MODELS.md) | ||||
| CREATE TABLE IF NOT EXISTS Persons ( | ||||
| 	id TEXT(36),    /* UUIDv4 */ | ||||
| 	remote_id TEXT, /* field to store an arbitrary remote identifier for this Person if they are not local */ | ||||
| 	name TEXT,      /* "real" name */ | ||||
| 	handle TEXT,    /* also commonly refered to as 'display_name' or 'screen_name', optional (if missing, name will be used as handle) */ | ||||
| 	avatar TEXT,	/* optional URL to Person's avatar image / profile pic */ | ||||
| 	cover TEXT,		/* optional URL to Person's cover image / header image */ | ||||
| 	bio TEXT,		/* optional biographical text provided by Person */ | ||||
| 	is_active INTEGER DEFAULT (1),    /* bool, default true */ | ||||
| 	is_blocked INTEGER DEFAULT (0),   /* bool, default false */ | ||||
| 	created_at INTEGER,               /* timestamp */ | ||||
| 	modified_at INTEGER,              /* timestamp */ | ||||
| 	modified_by TEXT(36),             /* UUIDv4 */ | ||||
| 	deleted_at INTEGER,               /* timestamp */ | ||||
| 	deleted_by TEXT(36),              /* UUIDv4 */ | ||||
| 	last_seen INTEGER,                /* timestamp */ | ||||
| 	/*shipping_address TEXT,*/            /* optional, should use \n between lines to keep localized format as needed - UPDATE: moving to lookup table */ | ||||
| 	CONSTRAINT Persons_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Persons_FK FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Persons_FK_1 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS Persons_remote_id_IDX ON Persons (remote_id); | ||||
| CREATE INDEX IF NOT EXISTS Persons_name_IDX ON Persons (name); | ||||
| CREATE INDEX IF NOT EXISTS Persons_handle_IDX ON Persons (handle); | ||||
| CREATE INDEX IF NOT EXISTS Persons_is_active_IDX ON Persons (is_active); | ||||
| CREATE INDEX IF NOT EXISTS Persons_is_blocked_IDX ON Persons (is_blocked); | ||||
| CREATE INDEX IF NOT EXISTS Persons_deleted_at_IDX ON Persons (deleted_at); | ||||
|  | ||||
| -- The PersonShippingAddresses table is a one-to-many lookup table relating a Person to zero or more shipping addresses  | ||||
| CREATE TABLE IF NOT EXISTS PersonShippingAddresses ( | ||||
|     id TEXT(36),                        /* UUIDv4 */ | ||||
|     person_id TEXT(36),                 /* UUIDv4 */  | ||||
|     shipping_address TEXT,              /* mandatory, should use \n between lines to keep localized format as needed */  | ||||
|     is_primary INTEGER DEFAULT (0),     /* bool, default false */  | ||||
|     created_at INTEGER,                 /* timestamp */  | ||||
|     modified_at INTEGER,                /* timestamp */  | ||||
|     modified_by TEXT(36),               /* UUIDv4 */  | ||||
|     deleted_at INTEGER,                 /* timestamp */  | ||||
|     deleted_by TEXT(36),                /* UUIDv4 */  | ||||
|     CONSTRAINT PersonShippingAddresses_PK PRIMARY KEY (id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK FOREIGN KEY (person_id) REFERENCES Persons(id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
|     CONSTRAINT PersonShippingAddresses_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS PersonShippingAddresses_is_primary_IDX ON PersonShippingAddresses (is_primary); | ||||
|  | ||||
| -- The PersonEmails table is a one-to-many lookup table relating a Person to zero or more email addresses | ||||
| CREATE TABLE IF NOT EXISTS PersonEmails ( | ||||
| 	person_id TEXT(36), | ||||
| 	email TEXT, | ||||
| 	is_verified INTEGER DEFAULT (0), | ||||
| 	is_primary INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT PersonEmails_PK PRIMARY KEY (email), | ||||
| 	CONSTRAINT PersonEmails_FK FOREIGN KEY (person_id) REFERENCES Persons(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PersonEmails_person_id_IDX ON PersonEmails (person_id); | ||||
| CREATE INDEX IF NOT EXISTS PersonEmails_is_verified_IDX ON PersonEmails (is_verified); | ||||
| CREATE INDEX IF NOT EXISTS PersonEmails_is_primary_IDX ON PersonEmails (is_primary); | ||||
|  | ||||
| -- The PersonCredentialProvider table will contain configuration for different authentication providers. | ||||
| CREATE TABLE IF NOT EXISTS PersonCredentialProvider ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	"type" TEXT, | ||||
| 	config TEXT, | ||||
| 	CONSTRAINT PersonCredentialProvider_PK PRIMARY KEY (id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PersonCredentialProvider_name_IDX ON PersonCredentialProvider (name); | ||||
| CREATE INDEX IF NOT EXISTS PersonCredentialProvider_type_IDX ON PersonCredentialProvider ("type"); | ||||
|  | ||||
| -- The PersonCredential table will contain authentication credentials for Persons | ||||
| CREATE TABLE IF NOT EXISTS PersonCredential ( | ||||
| 	id TEXT(36), | ||||
| 	person_id TEXT(36), | ||||
| 	provider_id TEXT(36), | ||||
| 	provider_user_id TEXT(36), | ||||
| 	is_enabled INTEGER DEFAULT (1), | ||||
| 	CONSTRAINT PersonCredential_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT PersonCredential_FK FOREIGN KEY (person_id) REFERENCES Persons(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT PersonCredential_FK_1 FOREIGN KEY (provider_id) REFERENCES PersonCredentialProvider(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PersonCredential_person_id_IDX ON PersonCredential (person_id); | ||||
| CREATE INDEX IF NOT EXISTS PersonCredential_provider_id_IDX ON PersonCredential (provider_id); | ||||
| CREATE INDEX IF NOT EXISTS PersonCredential_is_enabled_IDX ON PersonCredential (is_enabled); | ||||
|  | ||||
| -- The PersonLocal table will contain hashed and salted passwords for users authenticating via PersonCredentialProvider.Local type provider | ||||
| CREATE TABLE IF NOT EXISTS PersonLocal ( | ||||
| 	person_id TEXT(36), | ||||
| 	hash TEXT, | ||||
| 	modified_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	CONSTRAINT PersonLocal_PK PRIMARY KEY (person_id), | ||||
| 	CONSTRAINT PersonLocal_FK FOREIGN KEY (person_id) REFERENCES Persons(id) ON DELETE CASCADE | ||||
| ); | ||||
|  | ||||
| -- The Tags table will contain tags for categorization of other entities. Tags use the originating host and the tag name as a composite key to allow for us to search for commonalities across instances (for example, "show me Albums matching *::tag::jazz" to see all the jazz albums this instance is aware of, "show me Albums matching sundog-isle.reclaim.technology::tag::jazz" to see only albums tagged as jazz on that one specific instance named in the host field). | ||||
| CREATE TABLE IF NOT EXISTS Tags ( | ||||
| 	id TEXT(36), | ||||
| 	host TEXT, | ||||
| 	tag TEXT, | ||||
| 	CONSTRAINT Tags_PK PRIMARY KEY (id) | ||||
| ); | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS Tags_host_IDX ON Tags (host,tag); | ||||
|  | ||||
| -- The Labels table will contain data about record labels/imprints that manage one or more Artists and their content. | ||||
| CREATE TABLE IF NOT EXISTS Labels ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	description TEXT, | ||||
| 	website TEXT, | ||||
| 	is_enabled INTEGER DEFAULT (1), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	CONSTRAINT Labels_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Labels_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Labels_FK_1 FOREIGN KEY (modified_at) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Labels_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Labels_name_IDX ON Labels (name); | ||||
| CREATE INDEX IF NOT EXISTS Labels_description_IDX ON Labels (description); | ||||
| CREATE INDEX IF NOT EXISTS Labels_is_enabled_IDX ON Labels (is_enabled); | ||||
| CREATE INDEX IF NOT EXISTS Labels_created_by_IDX ON Labels (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Labels_deleted_at_IDX ON Labels (deleted_at); | ||||
|  | ||||
| -- The LabelContacts table will contain key-value pairs of methods to contact representatives of the Label (for example, "fediverse: sundog@toot-lab.reclaim.technology" or "email: sundog@reclaim.technology"). | ||||
| CREATE TABLE IF NOT EXISTS LabelContacts ( | ||||
| 	id TEXT(36), | ||||
| 	label_id TEXT(36), | ||||
| 	"method" TEXT, | ||||
| 	address TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	sort_order INTEGER, | ||||
| 	CONSTRAINT LabelContacts_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT LabelContacts_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT LabelContacts_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT LabelContacts_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT LabelContacts_FK_3 FOREIGN KEY (label_id) REFERENCES Labels(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS LabelContacts_sort_order_IDX ON LabelContacts (sort_order); | ||||
|  | ||||
| -- The LabelTags table will contain Tags that have been assigned to a Label. | ||||
| CREATE TABLE IF NOT EXISTS LabelTags ( | ||||
| 	label_id TEXT(36), | ||||
| 	tag_id TEXT(36), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT LabelTags_PK PRIMARY KEY (label_id,tag_id), | ||||
| 	CONSTRAINT LabelTags_FK FOREIGN KEY (label_id) REFERENCES Labels(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT LabelTags_FK_1 FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS LabelTags_is_approved_IDX ON LabelTags (is_approved); | ||||
| CREATE INDEX IF NOT EXISTS LabelTags_tag_id_IDX ON LabelTags (tag_id); | ||||
| CREATE INDEX IF NOT EXISTS LabelTags_label_id_IDX ON LabelTags (label_id); | ||||
|  | ||||
| -- The Artists table will contain Artists! | ||||
| CREATE TABLE IF NOT EXISTS Artists ( | ||||
| 	id TEXT(36), | ||||
| 	label_id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	bio TEXT, | ||||
| 	website TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	is_enabled INTEGER DEFAULT (1), | ||||
| 	is_public INTEGER DEFAULT (1), | ||||
| 	CONSTRAINT Artists_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Artists_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Artists_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Artists_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Artists_name_IDX ON Artists (name); | ||||
| CREATE INDEX IF NOT EXISTS Artists_is_enabled_IDX ON Artists (is_enabled); | ||||
| CREATE INDEX IF NOT EXISTS Artists_is_public_IDX ON Artists (is_public); | ||||
|  | ||||
| -- The ArtistContacts tablekey-value pairs of methods to contact representatives of the Artist (for example, "fediverse: sundog@toot-lab.reclaim.technology" or "email: sundog@reclaim.technology"). | ||||
| CREATE TABLE IF NOT EXISTS ArtistContacts ( | ||||
| 	id TEXT(36), | ||||
| 	artist_id TEXT(36), | ||||
| 	"method" TEXT, | ||||
| 	address TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	sort_order INTEGER, | ||||
| 	CONSTRAINT ArtistContacts_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT ArtistContacts_FK FOREIGN KEY (artist_id) REFERENCES Artists(id) ON DELETE CASCADE | ||||
| 	CONSTRAINT ArtistContacts_FK_1 FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT ArtistContacts_FK_2 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT ArtistContacts_FK_3 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS ArtistContacts_artist_id_IDX ON ArtistContacts (artist_id); | ||||
| CREATE INDEX IF NOT EXISTS ArtistContacts_deleted_at_IDX ON ArtistContacts (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS ArtistContacts_sort_order_IDX ON ArtistContacts (sort_order); | ||||
|  | ||||
| -- The ArtistTags table will contain Tags that have been assigned to an Artist. | ||||
| CREATE TABLE IF NOT EXISTS ArtistTags ( | ||||
| 	artist_id TEXT(36), | ||||
| 	tag_id TEXT(36), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT ArtistTags_PK PRIMARY KEY (artist_id,tag_id), | ||||
| 	CONSTRAINT ArtistTags_FK FOREIGN KEY (artist_id) REFERENCES Artists(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT ArtistTags_FK_1 FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS ArtistTags_is_approved_IDX ON ArtistTags (is_approved); | ||||
| CREATE INDEX IF NOT EXISTS ArtistTags_artist_id_IDX ON ArtistTags (artist_id); | ||||
| CREATE INDEX IF NOT EXISTS ArtistTags_tag_id_IDX ON ArtistTags (tag_id); | ||||
|  | ||||
| -- The Tracks table will contain Tracks! | ||||
| CREATE TABLE IF NOT EXISTS Tracks ( | ||||
| 	id TEXT(36), | ||||
| 	title TEXT, | ||||
| 	description TEXT, | ||||
| 	duration REAL, | ||||
| 	is_public INTEGER, | ||||
| 	is_available INTEGER DEFAULT (1), | ||||
| 	preview_source TEXT, | ||||
| 	"source" TEXT, | ||||
| 	price INTEGER DEFAULT (0), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	lyrics TEXT, | ||||
| 	CONSTRAINT Tracks_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Tracks_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Tracks_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Tracks_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_title_IDX ON Tracks (title); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_duration_IDX ON Tracks (duration); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_is_public_IDX ON Tracks (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_is_available_IDX ON Tracks (is_available); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_price_IDX ON Tracks (price); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_created_by_IDX ON Tracks (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_deleted_at_IDX ON Tracks (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS Tracks_lyrics_IDX ON Tracks (lyrics); | ||||
|  | ||||
| -- The TrackArtists table will be a one-to-many lookup table mapping Artists to a particular Track. | ||||
| CREATE TABLE IF NOT EXISTS TrackArtists ( | ||||
| 	track_id TEXT(36), | ||||
| 	artist_id TEXT(36), | ||||
| 	is_primary INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT TrackArtists_PK PRIMARY KEY (track_id,artist_id), | ||||
| 	CONSTRAINT TrackArtists_FK FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT TrackArtists_FK_1 FOREIGN KEY (artist_id) REFERENCES Artists(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS TrackArtists_is_primary_IDX ON TrackArtists (is_primary); | ||||
| CREATE INDEX IF NOT EXISTS TrackArtists_track_id_IDX ON TrackArtists (track_id); | ||||
| CREATE INDEX IF NOT EXISTS TrackArtists_artist_id_IDX ON TrackArtists (artist_id); | ||||
|  | ||||
| -- The TrackTags table will contain Tags that have been assigned to a Track. | ||||
| CREATE TABLE IF NOT EXISTS TrackTags ( | ||||
| 	track_id TEXT(36), | ||||
| 	tag_id TEXT(36), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT TrackTags_PK PRIMARY KEY (track_id,tag_id), | ||||
| 	CONSTRAINT TrackTags_FK FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT TrackTags_FK_1 FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS TrackTags_is_approved_IDX ON TrackTags (is_approved); | ||||
| CREATE INDEX IF NOT EXISTS TrackTags_track_id_IDX ON TrackTags (track_id); | ||||
| CREATE INDEX IF NOT EXISTS TrackTags_tag_id_IDX ON TrackTags (tag_id); | ||||
|  | ||||
| -- The Albums table will contain Albums! | ||||
| CREATE TABLE IF NOT EXISTS Albums ( | ||||
| 	id TEXT(36), | ||||
| 	title TEXT, | ||||
| 	description TEXT, | ||||
| 	is_public INTEGER DEFAULT (0), | ||||
| 	is_available INTEGER DEFAULT (1), | ||||
| 	preview_source TEXT, | ||||
| 	"source" TEXT, | ||||
| 	price INTEGER DEFAULT (0), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	CONSTRAINT Albums_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Albums_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Albums_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Albums_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Albums_title_IDX ON Albums (title); | ||||
| CREATE INDEX IF NOT EXISTS Albums_description_IDX ON Albums (description); | ||||
| CREATE INDEX IF NOT EXISTS Albums_is_public_IDX ON Albums (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Albums_is_available_IDX ON Albums (is_available); | ||||
| CREATE INDEX IF NOT EXISTS Albums_price_IDX ON Albums (price); | ||||
| CREATE INDEX IF NOT EXISTS Albums_created_by_IDX ON Albums (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Albums_deleted_at_IDX ON Albums (deleted_at); | ||||
|  | ||||
| -- The AlbumArtists table will be a one-to-many lookup table mapping Artists to a particular Album. | ||||
| CREATE TABLE IF NOT EXISTS AlbumArtists ( | ||||
| 	album_id TEXT(36), | ||||
| 	artist_id TEXT(36), | ||||
| 	is_primary INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT AlbumArtists_PK PRIMARY KEY (album_id,artist_id), | ||||
| 	CONSTRAINT AlbumArtists_FK FOREIGN KEY (album_id) REFERENCES Albums(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT AlbumArtists_FK_1 FOREIGN KEY (artist_id) REFERENCES Artists(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS AlbumArtists_is_primary_IDX ON AlbumArtists (is_primary); | ||||
| CREATE INDEX IF NOT EXISTS AlbumArtists_album_id_IDX ON AlbumArtists (album_id); | ||||
| CREATE INDEX IF NOT EXISTS AlbumArtists_artist_id_IDX ON AlbumArtists (artist_id); | ||||
|  | ||||
| -- The AlbumTags table will contain Tags that have been assigned to an Album. | ||||
| CREATE TABLE IF NOT EXISTS AlbumTags ( | ||||
| 	album_id TEXT(36), | ||||
| 	tag_id TEXT(36), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT AlbumTags_PK PRIMARY KEY (album_id,tag_id), | ||||
| 	CONSTRAINT AlbumTags_FK FOREIGN KEY (album_id) REFERENCES Albums(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT AlbumTags_FK_1 FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS AlbumTags_is_approved_IDX ON AlbumTags (is_approved); | ||||
| CREATE INDEX IF NOT EXISTS AlbumTags_album_id_IDX ON AlbumTags (album_id); | ||||
| CREATE INDEX IF NOT EXISTS AlbumTags_tag_id_IDX ON AlbumTags (tag_id); | ||||
|  | ||||
| -- The AlbumTracks table will be a many-to-many lookup table mapping Tracks to Albums | ||||
| CREATE TABLE IF NOT EXISTS AlbumTracks ( | ||||
| 	album_id TEXT(36), | ||||
| 	track_id TEXT(36), | ||||
| 	track_number INTEGER, | ||||
| 	CONSTRAINT AlbumTracks_PK PRIMARY KEY (album_id,track_id), | ||||
| 	CONSTRAINT AlbumTracks_FK FOREIGN KEY (album_id) REFERENCES Albums(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT AlbumTracks_FK_1 FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS AlbumTracks_track_number_IDX ON AlbumTracks (track_number); | ||||
| CREATE INDEX IF NOT EXISTS AlbumTracks_album_id_IDX ON AlbumTracks (album_id); | ||||
| CREATE INDEX IF NOT EXISTS AlbumTracks_track_id_IDX ON AlbumTracks (track_id); | ||||
|  | ||||
| -- The Playlists table will contain Playlists! | ||||
| CREATE TABLE IF NOT EXISTS Playlists ( | ||||
| 	id TEXT(36), | ||||
| 	title TEXT, | ||||
| 	description TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (1), | ||||
| 	CONSTRAINT Playlists_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Playlists_FK FOREIGN KEY (created_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_title_IDX ON Playlists (title); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_description_IDX ON Playlists (description); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_created_by_IDX ON Playlists (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_created_at_IDX ON Playlists (created_at); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_modified_at_IDX ON Playlists (modified_at); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_deleted_at_IDX ON Playlists (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS Playlists_is_public_IDX ON Playlists (is_public); | ||||
|  | ||||
| -- The PlaylistTracks table will be a many-to-many lookup table mapping Tracks to Playlists | ||||
| CREATE TABLE IF NOT EXISTS PlaylistTracks ( | ||||
| 	playlist_track_id TEXT(36), | ||||
| 	playlist_id TEXT(36), | ||||
| 	track_id TEXT, | ||||
| 	track_number INTEGER, | ||||
| 	CONSTRAINT PlaylistTracks_PK PRIMARY KEY (playlist_track_id), | ||||
| 	CONSTRAINT PlaylistTracks_FK FOREIGN KEY (playlist_id) REFERENCES Playlists(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT PlaylistTracks_FK_1 FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistTracks_track_number_IDX ON PlaylistTracks (track_number); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistTracks_playlist_id_IDX ON PlaylistTracks (playlist_id); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistTracks_track_id_IDX ON PlaylistTracks (track_id); | ||||
|  | ||||
| -- The PlaylistTags table will contain Tags that have been assigned to a Playlist. | ||||
| CREATE TABLE IF NOT EXISTS PlaylistTags ( | ||||
| 	playlist_id TEXT(36), | ||||
| 	tag_id TEXT(36), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT PlaylistTags_PK PRIMARY KEY (playlist_id,tag_id), | ||||
| 	CONSTRAINT PlaylistTags_FK FOREIGN KEY (playlist_id) REFERENCES Playlists(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT PlaylistTags_FK_1 FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistTags_is_approved_IDX ON PlaylistTags (is_approved); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistTags_playlist_id_IDX ON PlaylistTags (playlist_id); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistTags_tag_id_IDX ON PlaylistTags (tag_id); | ||||
|  | ||||
| -- The Comments table will contain Comments! | ||||
| CREATE TABLE IF NOT EXISTS Comments ( | ||||
| 	id TEXT(36), | ||||
| 	body TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (1), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	in_reply_to TEXT(36), | ||||
| 	CONSTRAINT Comments_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Comments_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Comments_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Comments_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Comments_body_IDX ON Comments (body); | ||||
| CREATE INDEX IF NOT EXISTS Comments_created_at_IDX ON Comments (created_at); | ||||
| CREATE INDEX IF NOT EXISTS Comments_modified_at_IDX ON Comments (modified_at); | ||||
| CREATE INDEX IF NOT EXISTS Comments_deleted_at_IDX ON Comments (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS Comments_is_public_IDX ON Comments (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Comments_is_approved_IDX ON Comments (is_approved); | ||||
| CREATE INDEX IF NOT EXISTS Comments_in_reply_to_IDX ON Comments (in_reply_to); | ||||
|  | ||||
| -- The LabelComments table will relate Comments to the Label they are about, if pertinent. | ||||
| CREATE TABLE IF NOT EXISTS LabelComments ( | ||||
| 	label_id TEXT(36), | ||||
| 	comment_id TEXT(36), | ||||
| 	CONSTRAINT LabelComments_PK PRIMARY KEY (label_id,comment_id), | ||||
| 	CONSTRAINT LabelComments_FK FOREIGN KEY (label_id) REFERENCES Labels(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT LabelComments_FK_1 FOREIGN KEY (comment_id) REFERENCES Comments(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS LabelComments_label_id_IDX ON LabelComments (label_id); | ||||
| CREATE INDEX IF NOT EXISTS LabelComments_comment_id_IDX ON LabelComments (comment_id); | ||||
|  | ||||
| -- The ArtistComments table will relate Comments to the Artist they are about, if pertinent. | ||||
| CREATE TABLE IF NOT EXISTS ArtistComments ( | ||||
| 	artist_id TEXT(36), | ||||
| 	comment_id TEXT(36), | ||||
| 	CONSTRAINT ArtistComments_PK PRIMARY KEY (artist_id,comment_id), | ||||
| 	CONSTRAINT ArtistComments_FK FOREIGN KEY (artist_id) REFERENCES Artists(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT ArtistComments_FK_1 FOREIGN KEY (comment_id) REFERENCES Comments(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS ArtistComments_artist_id_IDX ON ArtistComments (artist_id); | ||||
| CREATE INDEX IF NOT EXISTS ArtistComments_comment_id_IDX ON ArtistComments (comment_id); | ||||
|  | ||||
| -- The TrackComments table will relate Comments to the Track they are about, if pertinent. | ||||
| CREATE TABLE IF NOT EXISTS TrackComments ( | ||||
| 	track_id TEXT(36), | ||||
| 	comment_id TEXT(36), | ||||
| 	CONSTRAINT TrackComments_PK PRIMARY KEY (track_id,comment_id), | ||||
| 	CONSTRAINT TrackComments_FK FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT TrackComments_FK_1 FOREIGN KEY (comment_id) REFERENCES Comments(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS TrackComments_track_id_IDX ON TrackComments (track_id); | ||||
| CREATE INDEX IF NOT EXISTS TrackComments_comment_id_IDX ON TrackComments (comment_id); | ||||
|  | ||||
| -- The AlbumComments table will relate Comments to the Album they are about, if pertinent. | ||||
| CREATE TABLE IF NOT EXISTS AlbumComments ( | ||||
| 	album_id TEXT(36), | ||||
| 	comment_id TEXT(36), | ||||
| 	CONSTRAINT AlbumComments_PK PRIMARY KEY (album_id,comment_id), | ||||
| 	CONSTRAINT AlbumComments_FK FOREIGN KEY (album_id) REFERENCES Albums(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT AlbumComments_FK_1 FOREIGN KEY (comment_id) REFERENCES Comments(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS AlbumComments_album_id_IDX ON AlbumComments (album_id); | ||||
| CREATE INDEX IF NOT EXISTS AlbumComments_comment_id_IDX ON AlbumComments (comment_id); | ||||
|  | ||||
| -- The PlaylistComments table will relate Comments to the Playlist they are about, if pertinent. | ||||
| CREATE TABLE IF NOT EXISTS PlaylistComments ( | ||||
| 	playlist_id TEXT(36), | ||||
| 	comment_id TEXT(36), | ||||
| 	CONSTRAINT PlaylistComments_PK PRIMARY KEY (playlist_id,comment_id), | ||||
| 	CONSTRAINT PlaylistComments_FK FOREIGN KEY (playlist_id) REFERENCES Playlists(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT PlaylistComments_FK_1 FOREIGN KEY (comment_id) REFERENCES Comments(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistComments_playlist_id_IDX ON PlaylistComments (playlist_id); | ||||
| CREATE INDEX IF NOT EXISTS PlaylistComments_comment_id_IDX ON PlaylistComments (comment_id); | ||||
|  | ||||
| -- The Images table will contain Images! | ||||
| CREATE TABLE IF NOT EXISTS Images ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	"path" TEXT, | ||||
| 	url TEXT, | ||||
| 	width INTEGER, | ||||
| 	height INTEGER, | ||||
| 	alt_text TEXT, | ||||
| 	is_public INTEGER DEFAULT (1), | ||||
| 	allow_hotlinking INTEGER DEFAULT (0), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	CONSTRAINT Images_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Images_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Images_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Images_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Images_name_IDX ON Images (name); | ||||
| CREATE INDEX IF NOT EXISTS Images_path_IDX ON Images ("path"); | ||||
| CREATE INDEX IF NOT EXISTS Images_url_IDX ON Images (url); | ||||
| CREATE INDEX IF NOT EXISTS Images_alt_text_IDX ON Images (alt_text); | ||||
| CREATE INDEX IF NOT EXISTS Images_is_public_IDX ON Images (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Images_allow_hotlinking_IDX ON Images (allow_hotlinking); | ||||
| CREATE INDEX IF NOT EXISTS Images_created_by_IDX ON Images (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Images_deleted_at_IDX ON Images (deleted_at); | ||||
|  | ||||
| -- The Videos table will contain Videos! | ||||
| CREATE TABLE IF NOT EXISTS Videos ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	"path" TEXT, | ||||
| 	url TEXT, | ||||
| 	width INTEGER, | ||||
| 	height INTEGER, | ||||
| 	duration NUMERIC, | ||||
| 	alt_text TEXT, | ||||
| 	is_public INTEGER DEFAULT (1), | ||||
| 	allow_hotlinking INTEGER DEFAULT (0), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	CONSTRAINT Videos_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Videos_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Videos_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Videos_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Videos_name_IDX ON Videos (name); | ||||
| CREATE INDEX IF NOT EXISTS Videos_path_IDX ON Videos ("path"); | ||||
| CREATE INDEX IF NOT EXISTS Videos_url_IDX ON Videos (url); | ||||
| CREATE INDEX IF NOT EXISTS Videos_alt_text_IDX ON Videos (alt_text); | ||||
| CREATE INDEX IF NOT EXISTS Videos_is_public_IDX ON Videos (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Videos_allow_hotlinking_IDX ON Videos (allow_hotlinking); | ||||
| CREATE INDEX IF NOT EXISTS Videos_created_by_IDX ON Videos (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Videos_deleted_at_IDX ON Videos (deleted_at); | ||||
|  | ||||
| -- The Articles table will contain Articles! | ||||
| CREATE TABLE IF NOT EXISTS Articles ( | ||||
| 	id TEXT(36), | ||||
| 	title TEXT, | ||||
| 	body TEXT, | ||||
| 	description TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_at INTEGER, | ||||
| 	published_at INTEGER, | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (0), | ||||
| 	is_draft INTEGER DEFAULT (1), | ||||
| 	CONSTRAINT Articles_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Articles_FK FOREIGN KEY (created_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Articles_title_IDX ON Articles (title); | ||||
| CREATE INDEX IF NOT EXISTS Articles_body_IDX ON Articles (body); | ||||
| CREATE INDEX IF NOT EXISTS Articles_description_IDX ON Articles (description); | ||||
| CREATE INDEX IF NOT EXISTS Articles_created_by_IDX ON Articles (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Articles_published_at_IDX ON Articles (published_at); | ||||
| CREATE INDEX IF NOT EXISTS Articles_deleted_at_IDX ON Articles (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS Articles_is_public_IDX ON Articles (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Articles_is_draft_IDX ON Articles (is_draft); | ||||
|  | ||||
| -- The ArticleTags table will contain Tags that have been assigned to an Article. | ||||
| CREATE TABLE IF NOT EXISTS ArticleTags ( | ||||
| 	article_id TEXT(36), | ||||
| 	tag_id TEXT(36), | ||||
| 	is_approved INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT ArticleTags_PK PRIMARY KEY (article_id,tag_id), | ||||
| 	CONSTRAINT ArticleTags_FK FOREIGN KEY (article_id) REFERENCES Articles(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT ArticleTags_FK_1 FOREIGN KEY (tag_id) REFERENCES Tags(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS ArticleTags_is_approved_IDX ON ArticleTags (is_approved); | ||||
|  | ||||
| -- The ArticleComments table will relate Comments to the Article they are about, if pertinent. | ||||
| CREATE TABLE IF NOT EXISTS ArticleComments ( | ||||
| 	article_id TEXT(36), | ||||
| 	comment_id TEXT(36), | ||||
| 	CONSTRAINT ArticleComments_PK PRIMARY KEY (article_id,comment_id), | ||||
| 	CONSTRAINT ArticleComments_FK FOREIGN KEY (article_id) REFERENCES Articles(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT ArticleComments_FK_1 FOREIGN KEY (comment_id) REFERENCES Comments(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS ArticleComments_article_id_IDX ON ArticleComments (article_id); | ||||
| CREATE INDEX IF NOT EXISTS ArticleComments_comment_id_IDX ON ArticleComments (comment_id); | ||||
|  | ||||
| -- The OtherProducts table will contain products for sale that are not Albums nor Tracks nor Collections. | ||||
| CREATE TABLE IF NOT EXISTS OtherProducts ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	description TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (0), | ||||
| 	is_available INTEGER DEFAULT (0), | ||||
| 	requires_shipping INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT OtherProducts_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT OtherProducts_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT OtherProducts_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT OtherProducts_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_name_IDX ON OtherProducts (name); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_description_IDX ON OtherProducts (description); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_created_by_IDX ON OtherProducts (created_by); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_created_at_IDX ON OtherProducts (created_at); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_modified_by_IDX ON OtherProducts (modified_by); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_modified_at_IDX ON OtherProducts (modified_at); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_deleted_by_IDX ON OtherProducts (deleted_by); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_deleted_at_IDX ON OtherProducts (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_is_public_IDX ON OtherProducts (is_public); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_is_available_IDX ON OtherProducts (is_available); | ||||
| CREATE INDEX IF NOT EXISTS OtherProducts_requires_shipping_IDX ON OtherProducts (requires_shipping); | ||||
|  | ||||
| -- The Collections table will contain Collections where each Collection is comprised of one or more Tracks, Albums, and/or OtherProducts packaged together for sale. | ||||
| CREATE TABLE IF NOT EXISTS Collections ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	description TEXT, | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	is_public INTEGER DEFAULT (0), | ||||
| 	is_available INTEGER DEFAULT (0), | ||||
| 	CONSTRAINT Collections_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Collections_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Collections_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Collections_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Collections_name_IDX ON Collections (name); | ||||
| CREATE INDEX IF NOT EXISTS Collections_description_IDX ON Collections (description); | ||||
| CREATE INDEX IF NOT EXISTS Collections_created_by_IDX ON Collections (created_by); | ||||
| CREATE INDEX IF NOT EXISTS Collections_created_at_IDX ON Collections (created_at); | ||||
| CREATE INDEX IF NOT EXISTS Collections_modified_by_IDX ON Collections (modified_by); | ||||
| CREATE INDEX IF NOT EXISTS Collections_modified_at_IDX ON Collections (modified_at); | ||||
| CREATE INDEX IF NOT EXISTS Collections_deleted_by_IDX ON Collections (deleted_by); | ||||
| CREATE INDEX IF NOT EXISTS Collections_deleted_at_IDX ON Collections (deleted_at); | ||||
| CREATE INDEX IF NOT EXISTS Collections_is_public_IDX ON Collections (is_public); | ||||
| CREATE INDEX IF NOT EXISTS Collections_is_available_IDX ON Collections (is_available); | ||||
|  | ||||
| -- The CollectionAlbums table will relate a Collection to the Album(s) it contains. | ||||
| CREATE TABLE IF NOT EXISTS CollectionAlbums ( | ||||
| 	collection_id TEXT(36), | ||||
| 	album_id TEXT(36), | ||||
| 	CONSTRAINT CollectionAlbums_PK PRIMARY KEY (collection_id,album_id), | ||||
| 	CONSTRAINT CollectionAlbums_FK FOREIGN KEY (collection_id) REFERENCES Collections(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT CollectionAlbums_FK_1 FOREIGN KEY (album_id) REFERENCES Albums(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS CollectionAlbums_collection_id_IDX ON CollectionAlbums (collection_id); | ||||
| CREATE INDEX IF NOT EXISTS CollectionAlbums_album_id_IDX ON CollectionAlbums (album_id); | ||||
|  | ||||
| -- The CollectionTracks table will relate a Collection to the Track(s) it contains. | ||||
| CREATE TABLE IF NOT EXISTS CollectionTracks ( | ||||
| 	collection_id TEXT(36), | ||||
| 	track_id TEXT(36), | ||||
| 	CONSTRAINT CollectionTracks_PK PRIMARY KEY (collection_id,track_id), | ||||
| 	CONSTRAINT CollectionTracks_FK FOREIGN KEY (collection_id) REFERENCES Collections(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT CollectionTracks_FK_1 FOREIGN KEY (track_id) REFERENCES Tracks(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS CollectionTracks_collection_id_IDX ON CollectionTracks (collection_id); | ||||
| CREATE INDEX IF NOT EXISTS CollectionTracks_track_id_IDX ON CollectionTracks (track_id); | ||||
|  | ||||
| -- The CollectionOtherProducts table will relate a Collection to the OtherProduct(s) it contains. | ||||
| CREATE TABLE IF NOT EXISTS CollectionOtherProducts ( | ||||
| 	collection_id TEXT(36), | ||||
| 	other_product_id TEXT(36), | ||||
| 	CONSTRAINT CollectionOtherProducts_PK PRIMARY KEY (collection_id,other_product_id), | ||||
| 	CONSTRAINT CollectionOtherProducts_FK FOREIGN KEY (collection_id) REFERENCES Collections(id) ON DELETE CASCADE, | ||||
| 	CONSTRAINT CollectionOtherProducts_FK_1 FOREIGN KEY (other_product_id) REFERENCES OtherProducts(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS CollectionOtherProducts_collection_id_IDX ON CollectionOtherProducts (collection_id); | ||||
| CREATE INDEX IF NOT EXISTS CollectionOtherProducts_other_product_id_IDX ON CollectionOtherProducts (other_product_id); | ||||
|  | ||||
| -- The Skus table will contain SKUs () representing items available for Purchase through the server. | ||||
| CREATE TABLE IF NOT EXISTS Skus ( | ||||
| 	id TEXT(36), | ||||
| 	item_type TEXT, | ||||
| 	item_id TEXT(36), | ||||
| 	variant TEXT, | ||||
| 	description TEXT, | ||||
| 	price NUMERIC DEFAULT (0.00), | ||||
| 	requires_shipping INTEGER DEFAULT (0), | ||||
| 	shipping_charge NUMERIC DEFAULT (0.00), | ||||
| 	CONSTRAINT Skus_PK PRIMARY KEY (id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Skus_item_type_IDX ON Skus (item_type); | ||||
| CREATE INDEX IF NOT EXISTS Skus_item_id_IDX ON Skus (item_id); | ||||
| CREATE INDEX IF NOT EXISTS Skus_variant_IDX ON Skus (variant); | ||||
| CREATE INDEX IF NOT EXISTS Skus_description_IDX ON Skus (description); | ||||
| CREATE INDEX IF NOT EXISTS Skus_price_IDX ON Skus (price); | ||||
| CREATE INDEX IF NOT EXISTS Skus_requires_shipping_IDX ON Skus (requires_shipping); | ||||
| CREATE INDEX IF NOT EXISTS Skus_shipping_charge_IDX ON Skus (shipping_charge); | ||||
|  | ||||
| -- The LineItems table will contain individual SKUs and their associated quantites as part of a Purchase. | ||||
| CREATE TABLE IF NOT EXISTS LineItems ( | ||||
| 	id TEXT(36), | ||||
| 	sku_id TEXT(36), | ||||
| 	quantity INTEGER DEFAULT (1), | ||||
| 	CONSTRAINT LineItems_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT LineItems_FK FOREIGN KEY (sku_id) REFERENCES Skus(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS LineItems_sku_id_IDX ON LineItems (sku_id); | ||||
| CREATE INDEX IF NOT EXISTS LineItems_quantity_IDX ON LineItems (quantity); | ||||
|  | ||||
| -- The CouponCodes table will contain coupon codes that can be redeemed for a discount, either by amount or percentage, on a Purchase. | ||||
| CREATE TABLE IF NOT EXISTS CouponCodes ( | ||||
| 	id TEXT(36), | ||||
| 	name TEXT, | ||||
| 	code TEXT, | ||||
| 	uses INTEGER DEFAULT (0), | ||||
| 	max_uses INTEGER DEFAULT (0), | ||||
| 	expiration INTEGER, | ||||
| 	discount_flat NUMERIC DEFAULT (0.00), | ||||
| 	discount_percentage NUMERIC DEFAULT (0.00), | ||||
| 	is_sku_specific INTEGER DEFAULT (0), | ||||
| 	is_active INTEGER DEFAULT (0), | ||||
| 	created_by TEXT(36), | ||||
| 	created_at INTEGER, | ||||
| 	modified_by TEXT(36), | ||||
| 	modified_at INTEGER, | ||||
| 	deleted_by TEXT(36), | ||||
| 	deleted_at INTEGER, | ||||
| 	CONSTRAINT CouponCodes_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT CouponCodes_FK FOREIGN KEY (created_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT CouponCodes_FK_1 FOREIGN KEY (modified_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT CouponCodes_FK_2 FOREIGN KEY (deleted_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_name_IDX ON CouponCodes (name); | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS CouponCodes_code_IDX ON CouponCodes (code); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_uses_IDX ON CouponCodes (uses); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_max_uses_IDX ON CouponCodes (max_uses); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_expiration_IDX ON CouponCodes (expiration); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_is_sku_specific_IDX ON CouponCodes (is_sku_specific); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_is_active_IDX ON CouponCodes (is_active); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_created_by_IDX ON CouponCodes (created_by); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_created_at_IDX ON CouponCodes (created_at); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_modified_by_IDX ON CouponCodes (modified_by); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_modified_at_IDX ON CouponCodes (modified_at); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_deleted_by_IDX ON CouponCodes (deleted_by); | ||||
| CREATE INDEX IF NOT EXISTS CouponCodes_deleted_at_IDX ON CouponCodes (deleted_at); | ||||
|  | ||||
| -- The CouponCodeSkus table will relate a CouponCode to one or more Skus the CouponCode can be applied to, if CouponCode.is_sku_specific is true. (If CouponCode.is_sku_specific is false the CouponCode can be applied to an entire Purchase regardless of the Skus being purchased.) | ||||
| CREATE TABLE IF NOT EXISTS CouponCodeSkus ( | ||||
| 	coupon_code_id TEXT(36), | ||||
| 	sku_id TEXT(36), | ||||
| 	CONSTRAINT CouponCodeSkus_PK PRIMARY KEY (coupon_code_id,sku_id), | ||||
| 	CONSTRAINT CouponCodeSkus_FK FOREIGN KEY (coupon_code_id) REFERENCES CouponCodes(id), | ||||
| 	CONSTRAINT CouponCodeSkus_FK_1 FOREIGN KEY (sku_id) REFERENCES Skus(id) | ||||
| ); | ||||
|  | ||||
| -- The Purchases table will contain Purchases! | ||||
| CREATE TABLE IF NOT EXISTS Purchases ( | ||||
| 	id TEXT(36), | ||||
| 	state TEXT, | ||||
| 	purchased_by TEXT(36), | ||||
| 	purchased_at INTEGER, | ||||
| 	fulfilled_by TEXT(36), | ||||
| 	fulfilled_at INTEGER, | ||||
| 	total_charge NUMERIC DEFAULT (0.00), | ||||
| 	CONSTRAINT Purchases_PK PRIMARY KEY (id), | ||||
| 	CONSTRAINT Purchases_FK FOREIGN KEY (purchased_by) REFERENCES Persons(id), | ||||
| 	CONSTRAINT Purchases_FK_1 FOREIGN KEY (fulfilled_by) REFERENCES Persons(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS Purchases_state_IDX ON Purchases (state); | ||||
| CREATE INDEX IF NOT EXISTS Purchases_purchased_by_IDX ON Purchases (purchased_by); | ||||
| CREATE INDEX IF NOT EXISTS Purchases_purchased_at_IDX ON Purchases (purchased_at); | ||||
| CREATE INDEX IF NOT EXISTS Purchases_fulfilled_by_IDX ON Purchases (fulfilled_by); | ||||
| CREATE INDEX IF NOT EXISTS Purchases_fulfilled_at_IDX ON Purchases (fulfilled_at); | ||||
| CREATE INDEX IF NOT EXISTS Purchases_total_charge_IDX ON Purchases (total_charge); | ||||
|  | ||||
| -- The PurchaseLineItems table will relate a Purchase to one or more LineItems. | ||||
| CREATE TABLE IF NOT EXISTS PurchaseLineItems ( | ||||
| 	purchase_id TEXT(36), | ||||
| 	line_item_id TEXT(36), | ||||
| 	CONSTRAINT PurchaseLineItems_PK PRIMARY KEY (purchase_id,line_item_id), | ||||
| 	CONSTRAINT PurchaseLineItems_FK FOREIGN KEY (purchase_id) REFERENCES Purchases(id), | ||||
| 	CONSTRAINT PurchaseLineItems_FK_1 FOREIGN KEY (line_item_id) REFERENCES LineItems(id) ON DELETE CASCADE | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PurchaseLineItems_purchase_id_IDX ON PurchaseLineItems (purchase_id); | ||||
| CREATE INDEX IF NOT EXISTS PurchaseLineItems_line_item_id_IDX ON PurchaseLineItems (line_item_id); | ||||
|  | ||||
| -- The PurchaseCouponCodes table will relate a Purchase to one or more CouponCodes successfully applied to the Purchase. | ||||
| CREATE TABLE IF NOT EXISTS PurchaseCouponCodes ( | ||||
| 	purchase_id TEXT(36), | ||||
| 	coupon_code_id TEXT(36), | ||||
| 	CONSTRAINT PurchaseCouponCodes_PK PRIMARY KEY (purchase_id,coupon_code_id), | ||||
| 	CONSTRAINT PurchaseCouponCodes_FK FOREIGN KEY (purchase_id) REFERENCES Purchases(id), | ||||
| 	CONSTRAINT PurchaseCouponCodes_FK_1 FOREIGN KEY (coupon_code_id) REFERENCES CouponCodes(id) | ||||
| ); | ||||
| CREATE INDEX IF NOT EXISTS PurchaseCouponCodes_purchase_id_IDX ON PurchaseCouponCodes (purchase_id); | ||||
| CREATE INDEX IF NOT EXISTS PurchaseCouponCodes_coupon_code_id_IDX ON PurchaseCouponCodes (coupon_code_id); | ||||
							
								
								
									
										3
									
								
								pre-commit
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								pre-commit
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,3 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| cargo fmt --check | ||||
							
								
								
									
										564
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										564
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,565 +1,9 @@ | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::{collections::HashMap, sync::Arc}; | ||||
| use warp::{ | ||||
|     filters::{body::BodyDeserializeError, cors::CorsForbidden}, | ||||
|     http::Method, | ||||
|     http::StatusCode, | ||||
|     reject::Reject, | ||||
|     Filter, Rejection, Reply, | ||||
| }; | ||||
|  | ||||
| use tokio::sync::RwLock; | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Person { | ||||
|     id: PersonId, | ||||
|     remote_id: String, | ||||
|     name: String, | ||||
|     handle: Option<String>, | ||||
|     avatar: Option<String>, | ||||
|     cover: Option<String>, | ||||
|     bio: Option<String>, | ||||
|     is_active: bool, | ||||
|     is_blocked: bool, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     last_seen: usize, | ||||
|     shipping_addresses: Option<Vec<PersonShippingAddress>>, | ||||
|     emails: Option<Vec<PersonEmail>>, | ||||
|     credentials: Option<Vec<PersonCredential>>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct PersonId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct PersonShippingAddress { | ||||
|     id: PersonShippingAddressId, | ||||
|     person_id: PersonId, | ||||
|     shipping_address: String, | ||||
|     is_primary: bool, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct PersonShippingAddressId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct PersonEmail { | ||||
|     person_id: PersonId, | ||||
|     email: String, | ||||
|     is_verified: bool, | ||||
|     is_primary: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct PersonEmailId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct PersonCredentialProvider { | ||||
|     id: PersonCredentialProviderId, | ||||
|     name: String, | ||||
|     r#type: String, // TODO: create a type for this  | ||||
|     config: String, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct PersonCredentialProviderId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct PersonCredential { | ||||
|     id: PersonCredentialId, | ||||
|     person_id: PersonId, | ||||
|     provider_user_id: String, | ||||
|     is_enabled: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct PersonCredentialId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Tag { | ||||
|     id: TagId, | ||||
|     host: String, | ||||
|     tag: String, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct TagId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Label { | ||||
|     id: LabelId, | ||||
|     name: String, | ||||
|     description: String, | ||||
|     website: String, | ||||
|     is_enabled: bool, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     contacts: Option<Vec<LabelContact>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
|     artists: Option<Vec<Artist>>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct LabelId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct LabelContact { | ||||
|     id: LabelContactId, | ||||
|     label_id: LabelId, | ||||
|     method: String, // TODO: create a type for this  | ||||
|     address: String, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     sort_order: usize, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct LabelContactId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Artist { | ||||
|     id: ArtistId, | ||||
|     label_id: Option<LabelId>, | ||||
|     name: String, | ||||
|     bio: String, | ||||
|     website: String, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     is_enabled: bool, | ||||
|     is_public: bool, | ||||
|     contacts: Option<Vec<ArtistContact>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct ArtistId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct ArtistContact { | ||||
|     id: ArtistContactId, | ||||
|     artist_id: ArtistId, | ||||
|     method: String, // TODO: create a type for this | ||||
|     address: String, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     sort_order: usize, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct ArtistContactId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Track { | ||||
|     id: TrackId, | ||||
|     title: String, | ||||
|     description: String, | ||||
|     duration: f64, | ||||
|     is_public: bool, | ||||
|     is_available: bool, | ||||
|     preview_source: Option<String>, // if preview_source == None, use source as preview | ||||
|     source: String, | ||||
|     price: usize, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     lyrics: Option<String>, | ||||
|     primary_artist: Artist, | ||||
|     other_artists: Option<Vec<Artist>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct TrackId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Album { | ||||
|     id: AlbumId, | ||||
|     title: String, | ||||
|     description: String, | ||||
|     tracks: Vec<Track>, | ||||
|     is_public: bool, | ||||
|     is_available: bool, | ||||
|     preview_source: Option<String>, | ||||
|     source: String, | ||||
|     price: usize, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     created_by: PersonId, | ||||
|     modified_by: PersonId, | ||||
|     deleted_by: Option<PersonId>, | ||||
|     primary_artist: Option<Artist>, | ||||
|     other_artists: Option<Vec<Artist>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct AlbumId(String); | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone)] | ||||
| struct Playlist { | ||||
|     id: PlaylistId, | ||||
|     title: String, | ||||
|     description: String, | ||||
|     created_by: PersonId, | ||||
|     created_at: usize, | ||||
|     modified_at: usize, | ||||
|     deleted_at: Option<usize>, | ||||
|     is_public: bool, | ||||
|     tracks: Option<Vec<Track>>, | ||||
|     tags: Option<Vec<Tag>>, | ||||
| } | ||||
|  | ||||
| #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] | ||||
| struct PlaylistId(String); | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct Pagination { | ||||
|     start: usize, | ||||
|     end: usize, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| struct Datastore { | ||||
|     persons: Arc<RwLock<HashMap<PersonId, Person>>>, | ||||
|     labels: Arc<RwLock<HashMap<LabelId, Label>>>, | ||||
|     artists: Arc<RwLock<HashMap<ArtistId, Artist>>>, | ||||
|     albums: Arc<RwLock<HashMap<AlbumId, Album>>>, | ||||
|     tracks: Arc<RwLock<HashMap<TrackId, Track>>>, | ||||
| } | ||||
|  | ||||
| impl Datastore { | ||||
|     fn new() -> Self { | ||||
|         Datastore { | ||||
|             persons: Arc::new(RwLock::new(HashMap::new())), | ||||
|             labels: Arc::new(RwLock::new(HashMap::new())), | ||||
|             artists: Arc::new(RwLock::new(HashMap::new())), | ||||
|             albums: Arc::new(RwLock::new(HashMap::new())), | ||||
|             tracks: Arc::new(RwLock::new(HashMap::new())), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| enum Error { | ||||
|     ParseError(std::num::ParseIntError), | ||||
|     MissingParameters, | ||||
| } | ||||
|  | ||||
| impl std::fmt::Display for Error { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||
|         match *self { | ||||
|             Error::ParseError(ref err) => write!(f, "Cannot parse parameter: {}", err), | ||||
|             Error::MissingParameters => write!(f, "Missing parameter"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Reject for Error {} | ||||
|  | ||||
| async fn return_error(r: Rejection) -> Result<impl Reply, Rejection> { | ||||
|     if let Some(error) = r.find::<Error>() { | ||||
|         Ok(warp::reply::with_status( | ||||
|             error.to_string(), | ||||
|             StatusCode::RANGE_NOT_SATISFIABLE, | ||||
|         )) | ||||
|     } else if let Some(error) = r.find::<CorsForbidden>() { | ||||
|         Ok(warp::reply::with_status( | ||||
|             error.to_string(), | ||||
|             StatusCode::FORBIDDEN, | ||||
|         )) | ||||
|     } else if let Some(error) = r.find::<BodyDeserializeError>() { | ||||
|         Ok(warp::reply::with_status( | ||||
|             error.to_string(), | ||||
|             StatusCode::UNPROCESSABLE_ENTITY, | ||||
|         )) | ||||
|     } else { | ||||
|         Ok(warp::reply::with_status( | ||||
|             "Route not found".to_string(), | ||||
|             StatusCode::NOT_FOUND, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn extract_pagination(params: HashMap<String, String>) -> Result<Pagination, Error> { | ||||
|     if params.contains_key("start") && params.contains_key("end") { | ||||
|         return Ok(Pagination { | ||||
|             start: params | ||||
|                 .get("start") | ||||
|                 .unwrap() | ||||
|                 .parse::<usize>() | ||||
|                 .map_err(Error::ParseError)?, | ||||
|             end: params | ||||
|                 .get("end") | ||||
|                 .unwrap() | ||||
|                 .parse::<usize>() | ||||
|                 .map_err(Error::ParseError)?, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     Err(Error::MissingParameters) | ||||
| } | ||||
|  | ||||
| async fn get_persons( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Person> = datastore.persons.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Person> = datastore.persons.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_labels( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Label> = datastore.labels.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Label> = datastore.labels.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_artists( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Artist> = datastore.artists.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Artist> = datastore.artists.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_albums( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Album> = datastore.albums.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Album> = datastore.albums.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn get_tracks( | ||||
|     params: HashMap<String, String>, | ||||
|     datastore: Datastore, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     if !params.is_empty() { | ||||
|         let pagination = extract_pagination(params)?; | ||||
|         let res: Vec<Track> = datastore.tracks.read().await.values().cloned().collect(); | ||||
|         let res = &res[pagination.start..pagination.end]; | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } else { | ||||
|         let res: Vec<Track> = datastore.tracks.read().await.values().cloned().collect(); | ||||
|         Ok(warp::reply::json(&res)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn add_person( | ||||
|     datastore: Datastore, | ||||
|     person: Person, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .persons | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(person.id.clone(), person); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Person added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_label( | ||||
|     datastore: Datastore, | ||||
|     label: Label, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .labels | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(label.id.clone(), label); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Label added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_artist( | ||||
|     datastore: Datastore, | ||||
|     artist: Artist, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .artists | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(artist.id.clone(), artist); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Artist added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_album( | ||||
|     datastore: Datastore, | ||||
|     album: Album, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .albums | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(album.id.clone(), album); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Album added", StatusCode::OK)) | ||||
| } | ||||
|  | ||||
| async fn add_track( | ||||
|     datastore: Datastore, | ||||
|     track: Track, | ||||
| ) -> Result<impl warp::Reply, warp::Rejection> { | ||||
|     datastore | ||||
|         .tracks | ||||
|         .write() | ||||
|         .await | ||||
|         .insert(track.id.clone(), track); | ||||
|  | ||||
|     Ok(warp::reply::with_status("Track added", StatusCode::OK)) | ||||
| } | ||||
| use warp::Filter; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     let datastore = Datastore::new(); | ||||
|     let datastore_filter = warp::any().map(move || datastore.clone()); | ||||
|     let hello = warp::get() | ||||
|         .map(|| format!("Hello world!")); | ||||
|  | ||||
|     let cors = warp::cors() | ||||
|         .allow_any_origin() | ||||
|         .allow_header("content-type") | ||||
|         .allow_methods(&[Method::PUT, Method::DELETE, Method::GET, Method::POST]); | ||||
|  | ||||
|     let get_persons = warp::get() | ||||
|         .and(warp::path("persons")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_persons); | ||||
|  | ||||
|     let get_labels = warp::get() | ||||
|         .and(warp::path("labels")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_labels); | ||||
|  | ||||
|     let get_artists = warp::get() | ||||
|         .and(warp::path("artists")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_artists); | ||||
|      | ||||
|     let get_albums = warp::get() | ||||
|         .and(warp::path("albums")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_albums); | ||||
|  | ||||
|     let get_tracks = warp::get() | ||||
|         .and(warp::path("tracks")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(warp::query()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and_then(get_tracks); | ||||
|  | ||||
|     let add_person = warp::post() | ||||
|         .and(warp::path("persons")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_person); | ||||
|  | ||||
|     let add_label = warp::post() | ||||
|         .and(warp::path("labels")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_label); | ||||
|  | ||||
|     let add_artist = warp::post() | ||||
|         .and(warp::path("artists")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_artist); | ||||
|  | ||||
|     let add_album = warp::post() | ||||
|         .and(warp::path("albums")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_album); | ||||
|  | ||||
|     let add_track = warp::post() | ||||
|         .and(warp::path("tracks")) | ||||
|         .and(warp::path::end()) | ||||
|         .and(datastore_filter.clone()) | ||||
|         .and(warp::body::json()) | ||||
|         .and_then(add_track); | ||||
|  | ||||
|     let routes = get_persons | ||||
|         .or(get_labels) | ||||
|         .or(get_artists) | ||||
|         .or(get_albums) | ||||
|         .or(get_tracks) | ||||
|         .or(add_person) | ||||
|         .or(add_label) | ||||
|         .or(add_artist) | ||||
|         .or(add_album) | ||||
|         .or(add_track) | ||||
|         .with(cors) | ||||
|         .recover(return_error); | ||||
|  | ||||
|     warp::serve(routes).run(([127, 0, 0, 1], 5309)).await; | ||||
|     warp::serve(hello).run(([127, 0, 0, 1], 5309)).await; | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	