fat: add create product logic
This commit is contained in:
parent
5ad070684d
commit
5328ce170c
10 changed files with 446 additions and 17 deletions
53
crud.py
53
crud.py
|
|
@ -5,7 +5,16 @@ from typing import List, Optional
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
from .models import Merchant, PartialMerchant, PartialStall, PartialZone, Stall, Zone
|
from .models import (
|
||||||
|
Merchant,
|
||||||
|
PartialMerchant,
|
||||||
|
PartialProduct,
|
||||||
|
PartialStall,
|
||||||
|
PartialZone,
|
||||||
|
Product,
|
||||||
|
Stall,
|
||||||
|
Zone,
|
||||||
|
)
|
||||||
|
|
||||||
######################################## MERCHANT ########################################
|
######################################## MERCHANT ########################################
|
||||||
|
|
||||||
|
|
@ -177,3 +186,45 @@ async def delete_stall(user_id: str, stall_id: str) -> None:
|
||||||
stall_id,
|
stall_id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
######################################## STALL ########################################
|
||||||
|
|
||||||
|
|
||||||
|
async def create_product(user_id: str, data: PartialProduct) -> Product:
|
||||||
|
product_id = urlsafe_short_hash()
|
||||||
|
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
INSERT INTO nostrmarket.products (user_id, id, stall_id, name, category_list, description, images, price, quantity)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
user_id,
|
||||||
|
product_id,
|
||||||
|
data.stall_id,
|
||||||
|
data.name,
|
||||||
|
json.dumps(data.categories),
|
||||||
|
data.description,
|
||||||
|
data.image,
|
||||||
|
data.price,
|
||||||
|
data.quantity,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
product = await get_product(user_id, product_id)
|
||||||
|
assert product, "Newly created product couldn't be retrieved"
|
||||||
|
|
||||||
|
return product
|
||||||
|
|
||||||
|
|
||||||
|
async def get_product(user_id: str, product_id: str) -> Optional[Product]:
|
||||||
|
row = await db.fetchone(
|
||||||
|
"SELECT * FROM nostrmarket.products WHERE user_id =? AND id = ?",
|
||||||
|
(
|
||||||
|
user_id,
|
||||||
|
product_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
product = Product.from_row(row) if row else None
|
||||||
|
|
||||||
|
return product
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,13 @@ async def m001_initial(db):
|
||||||
await db.execute(
|
await db.execute(
|
||||||
f"""
|
f"""
|
||||||
CREATE TABLE nostrmarket.products (
|
CREATE TABLE nostrmarket.products (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
stall_id TEXT NOT NULL,
|
stall_id TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
categories TEXT,
|
category_list TEXT DEFAULT '[]',
|
||||||
description TEXT,
|
description TEXT,
|
||||||
images TEXT NOT NULL DEFAULT '[]',
|
images TEXT DEFAULT '[]',
|
||||||
price REAL NOT NULL,
|
price REAL NOT NULL,
|
||||||
quantity INTEGER NOT NULL
|
quantity INTEGER NOT NULL
|
||||||
);
|
);
|
||||||
|
|
|
||||||
42
models.py
42
models.py
|
|
@ -116,3 +116,45 @@ class Stall(PartialStall):
|
||||||
stall.config = StallConfig(**json.loads(row["meta"]))
|
stall.config = StallConfig(**json.loads(row["meta"]))
|
||||||
stall.shipping_zones = [Zone(**z) for z in json.loads(row["zones"])]
|
stall.shipping_zones = [Zone(**z) for z in json.loads(row["zones"])]
|
||||||
return stall
|
return stall
|
||||||
|
|
||||||
|
|
||||||
|
######################################## STALLS ########################################
|
||||||
|
|
||||||
|
|
||||||
|
class PartialProduct(BaseModel):
|
||||||
|
stall_id: str
|
||||||
|
name: str
|
||||||
|
categories: List[str] = []
|
||||||
|
description: Optional[str]
|
||||||
|
image: Optional[str]
|
||||||
|
price: float
|
||||||
|
quantity: int
|
||||||
|
|
||||||
|
def validate_product(self):
|
||||||
|
if self.image:
|
||||||
|
image_is_url = self.image.startswith("https://") or self.image.startswith(
|
||||||
|
"http://"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not image_is_url:
|
||||||
|
|
||||||
|
def size(b64string):
|
||||||
|
return int((len(b64string) * 3) / 4 - b64string.count("=", -2))
|
||||||
|
|
||||||
|
image_size = size(self.image) / 1024
|
||||||
|
if image_size > 100:
|
||||||
|
raise ValueError(
|
||||||
|
f"""
|
||||||
|
Image size is too big, {int(image_size)}Kb.
|
||||||
|
Max: 100kb, Compress the image at https://tinypng.com, or use an URL."""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Product(PartialProduct):
|
||||||
|
id: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_row(cls, row: Row) -> "Product":
|
||||||
|
product = cls(**dict(row))
|
||||||
|
product.categories = json.loads(row["category_list"])
|
||||||
|
return product
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
:label="'Amount (' + zoneDialog.data.currency + ') *'"
|
:label="'Cost of Shipping (' + zoneDialog.data.currency + ') *'"
|
||||||
fill-mask="0"
|
fill-mask="0"
|
||||||
reverse-fill-mask
|
reverse-fill-mask
|
||||||
:step="zoneDialog.data.currency != 'sat' ? '0.01' : '1'"
|
:step="zoneDialog.data.currency != 'sat' ? '0.01' : '1'"
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,153 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="payment">
|
<q-tab-panel name="products">
|
||||||
<div v-if="stall"></div>
|
<div v-if="stall">
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-3 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="green"
|
||||||
|
icon="plus"
|
||||||
|
class="float-left"
|
||||||
|
@click="showNewProductDialog()"
|
||||||
|
>New Product</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-sm-8 q-pr-lg"></div>
|
||||||
|
<div class="col-3 col-sm-1"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="config">
|
<q-tab-panel name="orders">
|
||||||
<div v-if="stall"></div>
|
<div v-if="stall"></div>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
|
<q-dialog v-model="productDialog.showDialog" position="top">
|
||||||
|
<q-card v-if="stall" class="q-pa-lg q-pt-xl" style="width: 500px">
|
||||||
|
<q-form @submit="sendProductFormData" class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="productDialog.data.name"
|
||||||
|
label="Name"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="productDialog.data.description"
|
||||||
|
label="Description"
|
||||||
|
></q-input>
|
||||||
|
<!-- <div class="row"> -->
|
||||||
|
<!-- <div class="col-5">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
multiple
|
||||||
|
v-model.trim="productDialog.data.categories"
|
||||||
|
:options="categories"
|
||||||
|
label="Categories"
|
||||||
|
class="q-pr-sm"
|
||||||
|
></q-select>
|
||||||
|
</div> -->
|
||||||
|
<!-- <div class="col-7"> -->
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
multiple
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
v-model.trim="productDialog.data.categories"
|
||||||
|
use-input
|
||||||
|
use-chips
|
||||||
|
multiple
|
||||||
|
hide-dropdown-icon
|
||||||
|
input-debounce="0"
|
||||||
|
new-value-mode="add-unique"
|
||||||
|
label="Categories (Hit Enter to add)"
|
||||||
|
placeholder="crafts,robots,etc"
|
||||||
|
></q-select>
|
||||||
|
<q-toggle
|
||||||
|
:label="`${productDialog.url ? 'Insert image URL' : 'Upload image file'}`"
|
||||||
|
v-model="productDialog.url"
|
||||||
|
></q-toggle>
|
||||||
|
<q-input
|
||||||
|
v-if="productDialog.url"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="productDialog.data.image"
|
||||||
|
type="url"
|
||||||
|
label="Image URL"
|
||||||
|
></q-input>
|
||||||
|
<q-file
|
||||||
|
v-else
|
||||||
|
class="q-pr-md"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
capture="environment"
|
||||||
|
accept="image/jpeg, image/png"
|
||||||
|
:max-file-size="3*1024**2"
|
||||||
|
label="Small image (optional)"
|
||||||
|
clearable
|
||||||
|
@input="imageAdded"
|
||||||
|
@clear="imageCleared"
|
||||||
|
>
|
||||||
|
<template v-if="productDialog.data.image" v-slot:before>
|
||||||
|
<img style="height: 1em" :src="productDialog.data.image" />
|
||||||
|
</template>
|
||||||
|
<template v-if="productDialog.data.image" v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
name="cancel"
|
||||||
|
@click.stop.prevent="imageCleared"
|
||||||
|
class="cursor-pointer"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-file>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="productDialog.data.price"
|
||||||
|
type="number"
|
||||||
|
:label="'Price (' + stall.currency + ') *'"
|
||||||
|
:step="stall.currency != 'sat' ? '0.01' : '1'"
|
||||||
|
:mask="stall.currency != 'sat' ? '#.##' : '#'"
|
||||||
|
fill-mask="0"
|
||||||
|
reverse-fill-mask
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="productDialog.data.quantity"
|
||||||
|
type="number"
|
||||||
|
label="Quantity"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
v-if="productDialog.data.id"
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
type="submit"
|
||||||
|
>Update Product</q-btn
|
||||||
|
>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
v-else
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="!productDialog.data.price
|
||||||
|
|| !productDialog.data.name
|
||||||
|
|| !productDialog.data.quantity"
|
||||||
|
type="submit"
|
||||||
|
>Create Product</q-btn
|
||||||
|
>
|
||||||
|
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||||
|
>Cancel</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
async function stallDetails(path) {
|
async function stallDetails(path) {
|
||||||
const template = await loadTemplateAsync(path)
|
const template = await loadTemplateAsync(path)
|
||||||
|
|
||||||
|
const pica = window.pica()
|
||||||
|
|
||||||
Vue.component('stall-details', {
|
Vue.component('stall-details', {
|
||||||
name: 'stall-details',
|
name: 'stall-details',
|
||||||
template,
|
template,
|
||||||
|
|
@ -16,8 +18,21 @@ async function stallDetails(path) {
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
tab: 'info',
|
tab: 'info',
|
||||||
stall: null
|
stall: null,
|
||||||
// currencies: [],
|
products: [],
|
||||||
|
productDialog: {
|
||||||
|
showDialog: false,
|
||||||
|
url: true,
|
||||||
|
data: {
|
||||||
|
id: null,
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
categories: [],
|
||||||
|
image: null,
|
||||||
|
price: 0,
|
||||||
|
quantity: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -96,6 +111,97 @@ async function stallDetails(path) {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
imageAdded(file) {
|
||||||
|
const image = new Image()
|
||||||
|
image.src = URL.createObjectURL(file)
|
||||||
|
image.onload = async () => {
|
||||||
|
let fit = imgSizeFit(image)
|
||||||
|
let canvas = document.createElement('canvas')
|
||||||
|
canvas.setAttribute('width', fit.width)
|
||||||
|
canvas.setAttribute('height', fit.height)
|
||||||
|
output = await pica.resize(image, canvas)
|
||||||
|
this.productDialog.data.image = output.toDataURL('image/jpeg', 0.4)
|
||||||
|
this.productDialog = {...this.productDialog}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
imageCleared() {
|
||||||
|
this.productDialog.data.image = null
|
||||||
|
this.productDialog = {...this.productDialog}
|
||||||
|
},
|
||||||
|
sendProductFormData: function () {
|
||||||
|
var data = {
|
||||||
|
stall_id: this.stall.id,
|
||||||
|
name: this.productDialog.data.name,
|
||||||
|
description: this.productDialog.data.description,
|
||||||
|
categories: this.productDialog.data.categories,
|
||||||
|
|
||||||
|
image: this.productDialog.data.image,
|
||||||
|
price: this.productDialog.data.price,
|
||||||
|
quantity: this.productDialog.data.quantity
|
||||||
|
}
|
||||||
|
this.productDialog.showDialog = false
|
||||||
|
if (this.productDialog.data.id) {
|
||||||
|
this.updateProduct(data)
|
||||||
|
} else {
|
||||||
|
this.createProduct(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateProduct: function (data) {
|
||||||
|
var self = this
|
||||||
|
let wallet = _.findWhere(this.stalls, {
|
||||||
|
id: self.productDialog.data.stall
|
||||||
|
}).wallet
|
||||||
|
LNbits.api
|
||||||
|
.request(
|
||||||
|
'PUT',
|
||||||
|
'/nostrmarket/api/v1/products/' + data.id,
|
||||||
|
_.findWhere(self.g.user.wallets, {
|
||||||
|
id: wallet
|
||||||
|
}).inkey,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
.then(async function (response) {
|
||||||
|
self.products = _.reject(self.products, function (obj) {
|
||||||
|
return obj.id == data.id
|
||||||
|
})
|
||||||
|
let productData = mapProducts(response.data)
|
||||||
|
self.products.push(productData)
|
||||||
|
//SEND Nostr data
|
||||||
|
try {
|
||||||
|
await self.sendToRelays(productData, 'product', 'update')
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
self.resetDialog('productDialog')
|
||||||
|
//self.productDialog.show = false
|
||||||
|
//self.productDialog.data = {}
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
createProduct: async function (payload) {
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'POST',
|
||||||
|
'/nostrmarket/api/v1/product',
|
||||||
|
this.adminkey,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
this.products.unshift(data)
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: 'Product Created',
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error)
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showNewProductDialog: async function () {
|
||||||
|
this.productDialog.showDialog = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: async function () {
|
created: async function () {
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,6 @@ async function stallList(path) {
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
field: 'id'
|
field: 'id'
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// name: 'toggle',
|
|
||||||
// align: 'left',
|
|
||||||
// label: 'Active',
|
|
||||||
// field: ''
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
|
@ -88,6 +82,7 @@ async function stallList(path) {
|
||||||
stall
|
stall
|
||||||
)
|
)
|
||||||
this.stallDialog.show = false
|
this.stallDialog.show = false
|
||||||
|
data.expanded = false
|
||||||
this.stalls.unshift(data)
|
this.stalls.unshift(data)
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
|
|
@ -154,6 +149,13 @@ async function stallList(path) {
|
||||||
openCreateStallDialog: async function () {
|
openCreateStallDialog: async function () {
|
||||||
await this.getCurrencies()
|
await this.getCurrencies()
|
||||||
await this.getZones()
|
await this.getZones()
|
||||||
|
if (!this.zoneOptions || !this.zoneOptions.length) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Please create a Shipping Zone first!'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
this.stallDialog.data = {
|
this.stallDialog.data = {
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,12 @@ function loadTemplateAsync(path) {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function imgSizeFit(img, maxWidth = 1024, maxHeight = 768) {
|
||||||
|
let ratio = Math.min(
|
||||||
|
1,
|
||||||
|
maxWidth / img.naturalWidth,
|
||||||
|
maxHeight / img.naturalHeight
|
||||||
|
)
|
||||||
|
return {width: img.naturalWidth * ratio, height: img.naturalHeight * ratio}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@
|
||||||
{% endblock%}{% block scripts %} {{ window_vars(user) }}
|
{% endblock%}{% block scripts %} {{ window_vars(user) }}
|
||||||
<!-- todo: serve locally -->
|
<!-- todo: serve locally -->
|
||||||
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
|
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
||||||
|
|
||||||
<script src="{{ url_for('nostrmarket_static', path='js/utils.js') }}"></script>
|
<script src="{{ url_for('nostrmarket_static', path='js/utils.js') }}"></script>
|
||||||
<script src="{{ url_for('nostrmarket_static', path='components/key-pair/key-pair.js') }}"></script>
|
<script src="{{ url_for('nostrmarket_static', path='components/key-pair/key-pair.js') }}"></script>
|
||||||
|
|
|
||||||
79
views_api.py
79
views_api.py
|
|
@ -17,6 +17,7 @@ from lnbits.utils.exchange_rates import currencies
|
||||||
from . import nostrmarket_ext
|
from . import nostrmarket_ext
|
||||||
from .crud import (
|
from .crud import (
|
||||||
create_merchant,
|
create_merchant,
|
||||||
|
create_product,
|
||||||
create_stall,
|
create_stall,
|
||||||
create_zone,
|
create_zone,
|
||||||
delete_stall,
|
delete_stall,
|
||||||
|
|
@ -29,7 +30,16 @@ from .crud import (
|
||||||
update_stall,
|
update_stall,
|
||||||
update_zone,
|
update_zone,
|
||||||
)
|
)
|
||||||
from .models import Merchant, PartialMerchant, PartialStall, PartialZone, Stall, Zone
|
from .models import (
|
||||||
|
Merchant,
|
||||||
|
PartialMerchant,
|
||||||
|
PartialProduct,
|
||||||
|
PartialStall,
|
||||||
|
PartialZone,
|
||||||
|
Product,
|
||||||
|
Stall,
|
||||||
|
Zone,
|
||||||
|
)
|
||||||
from .nostr.nostr_client import publish_nostr_event
|
from .nostr.nostr_client import publish_nostr_event
|
||||||
|
|
||||||
######################################## MERCHANT ########################################
|
######################################## MERCHANT ########################################
|
||||||
|
|
@ -170,6 +180,11 @@ async def api_create_stall(
|
||||||
await update_stall(wallet.wallet.user, stall)
|
await update_stall(wallet.wallet.user, stall)
|
||||||
|
|
||||||
return stall
|
return stall
|
||||||
|
except ValueError as ex:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=str(ex),
|
||||||
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning(ex)
|
logger.warning(ex)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -202,6 +217,11 @@ async def api_update_stall(
|
||||||
return stall
|
return stall
|
||||||
except HTTPException as ex:
|
except HTTPException as ex:
|
||||||
raise ex
|
raise ex
|
||||||
|
except ValueError as ex:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=str(ex),
|
||||||
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.warning(ex)
|
logger.warning(ex)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -271,10 +291,65 @@ async def api_delete_stall(
|
||||||
logger.warning(ex)
|
logger.warning(ex)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
detail="Cannot delte stall",
|
detail="Cannot delete stall",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
######################################## PRODUCTS ########################################
|
||||||
|
|
||||||
|
|
||||||
|
@nostrmarket_ext.post("/api/v1/product")
|
||||||
|
async def api_market_product_create(
|
||||||
|
data: PartialProduct,
|
||||||
|
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
|
) -> Product:
|
||||||
|
try:
|
||||||
|
data.validate_product()
|
||||||
|
product = await create_product(wallet.wallet.user, data=data)
|
||||||
|
|
||||||
|
return product
|
||||||
|
except ValueError as ex:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=str(ex),
|
||||||
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(ex)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Cannot create product",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# @nostrmarket_ext.get("/api/v1/product/{stall_id}")
|
||||||
|
# async def api_market_products(
|
||||||
|
# stall_id: str, wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||||
|
# ):
|
||||||
|
# wallet_ids = [wallet.wallet.id]
|
||||||
|
|
||||||
|
|
||||||
|
# return [product.dict() for product in await get_products(stalls)]
|
||||||
|
|
||||||
|
|
||||||
|
# @market_ext.delete("/api/v1/products/{product_id}")
|
||||||
|
# async def api_market_products_delete(
|
||||||
|
# product_id, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||||
|
# ):
|
||||||
|
# product = await get_market_product(product_id)
|
||||||
|
|
||||||
|
# if not product:
|
||||||
|
# return {"message": "Product does not exist."}
|
||||||
|
|
||||||
|
# stall = await get_market_stall(product.stall)
|
||||||
|
# assert stall
|
||||||
|
|
||||||
|
# if stall.wallet != wallet.wallet.id:
|
||||||
|
# return {"message": "Not your Market."}
|
||||||
|
|
||||||
|
# await delete_market_product(product_id)
|
||||||
|
# raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
######################################## OTHER ########################################
|
######################################## OTHER ########################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue