Compare commits
59 Commits
94ec4e7135
...
production
| Author | SHA1 | Date | |
|---|---|---|---|
| c611d03341 | |||
| db3038e181 | |||
| 241ee19e1c | |||
| b39391dc90 | |||
| 665a3c0dfa | |||
| eb50f45822 | |||
| 914f4ac97b | |||
| 02fcfba5f1 | |||
| be10d1896b | |||
| 0ed4f9545c | |||
| fcb195ddb3 | |||
| 6a3fd249a4 | |||
| f4b986c79e | |||
| 6efa4e8465 | |||
| 8fa0de6094 | |||
| 10b550a59a | |||
| b10292b880 | |||
| e94c54b2ad | |||
| a1ff1fc487 | |||
| cceed90772 | |||
| 11f7176bb6 | |||
| ebfcfbff2c | |||
|
|
6edff864fe | ||
| 18e7287a17 | |||
| 3c50565d5e | |||
| 4ccb6303f3 | |||
| 9030d1a8b0 | |||
| 7ae824b6fd | |||
| 2bc17e8cf4 | |||
|
|
897a46aa7b | ||
| 56d6b262ff | |||
| 3eccedfad4 | |||
| 8b1a96f133 | |||
| 4f64d998ef | |||
| 8aba087998 | |||
| 9a3143d2f0 | |||
| c4ee5b0394 | |||
| 643de642a1 | |||
|
|
42f294863c | ||
| 24e42f1d0b | |||
| 0e6caa136e | |||
| 49aed2493a | |||
| 3de787e07c | |||
| 1996ae6c73 | |||
| 4e6b5da71b | |||
| 2fa0f3cb8b | |||
| a2baa3d24c | |||
| 21425b22b5 | |||
| 0013bb18df | |||
| ef6305919d | |||
| 88442f22dd | |||
| 409f44476f | |||
| 8e2b41c0b3 | |||
| 4ff8c2cdfb | |||
| f37e556071 | |||
|
|
6337eb39ae | ||
| 5d57abe913 | |||
| c3b83d2041 | |||
| 47d62885b1 |
29
.dockerignore
Normal file
29
.dockerignore
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
**/.idea
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
**/bin
|
||||||
|
**/obj
|
||||||
|
front/node_modules
|
||||||
|
front/dist
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
Docs/
|
||||||
|
|
||||||
|
# Dev files
|
||||||
|
docker/
|
||||||
|
*.sh
|
||||||
|
|
||||||
|
# Env files (secrets must not be in the image)
|
||||||
|
.env
|
||||||
|
deploy/.env
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
**/*.user
|
||||||
|
**/*.DotSettings.user
|
||||||
BIN
Docs/Modeli komunikacije – Faza 3.docx
Normal file
BIN
Docs/Modeli komunikacije – Faza 3.docx
Normal file
Binary file not shown.
BIN
Docs/Modeli komunikacije – Faza 3.pdf
Normal file
BIN
Docs/Modeli komunikacije – Faza 3.pdf
Normal file
Binary file not shown.
76
Docs/dijagrami/communcation_components
Normal file
76
Docs/dijagrami/communcation_components
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.5.2 Chrome/142.0.7444.265 Electron/39.6.1 Safari/537.36" version="29.5.2">
|
||||||
|
<diagram name="Page-1" id="CTB3YmmPJF-eA0d3sg4m">
|
||||||
|
<mxGraphModel dx="1040" dy="1791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0" />
|
||||||
|
<mxCell id="1" parent="0" />
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-10" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="AipsWebApi" vertex="1">
|
||||||
|
<mxGeometry height="60" width="120" x="490" y="170" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-11" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" value="AipsRT" vertex="1">
|
||||||
|
<mxGeometry height="60" width="120" x="190" y="170" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-12" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="RabbitMQ" vertex="1">
|
||||||
|
<mxGeometry height="60" width="120" x="190" y="310" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-13" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=4;fillColor=#f8cecc;strokeColor=#b85450;" value="AipsWorker" vertex="1">
|
||||||
|
<mxGeometry height="60" width="120" x="190" y="450" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-14" parent="1" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" value="DB" vertex="1">
|
||||||
|
<mxGeometry height="80" width="60" x="520" y="440" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-16" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-13" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" target="9ORZMBqL03SGZpvbqk50-14" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="390" y="380" as="sourcePoint" />
|
||||||
|
<mxPoint x="440" y="330" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-17" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-14" style="shape=flexArrow;endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;" value="">
|
||||||
|
<mxGeometry height="100" relative="1" width="100" as="geometry">
|
||||||
|
<mxPoint x="460" y="330" as="sourcePoint" />
|
||||||
|
<mxPoint x="550" y="230" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-19" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-11" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="9ORZMBqL03SGZpvbqk50-12" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="390" y="380" as="sourcePoint" />
|
||||||
|
<mxPoint x="440" y="330" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-20" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-12" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="9ORZMBqL03SGZpvbqk50-13" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="309" y="380" as="sourcePoint" />
|
||||||
|
<mxPoint x="309" y="460" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-22" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-13" style="endArrow=classic;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" target="9ORZMBqL03SGZpvbqk50-12" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="390" y="380" as="sourcePoint" />
|
||||||
|
<mxPoint x="440" y="330" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-23" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-12" style="endArrow=classic;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" target="9ORZMBqL03SGZpvbqk50-11" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="390" y="380" as="sourcePoint" />
|
||||||
|
<mxPoint x="440" y="330" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-25" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="Klient" vertex="1">
|
||||||
|
<mxGeometry height="70" width="420" x="190" y="-40" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-27" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-11" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.142;entryY=1.015;entryDx=0;entryDy=0;entryPerimeter=0;" target="9ORZMBqL03SGZpvbqk50-25" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="290" y="140" as="sourcePoint" />
|
||||||
|
<mxPoint x="340" y="90" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9ORZMBqL03SGZpvbqk50-28" edge="1" parent="1" source="9ORZMBqL03SGZpvbqk50-10" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="390" y="210" as="sourcePoint" />
|
||||||
|
<mxPoint x="550" y="34" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
BIN
Docs/dijagrami/communication_components.png
Normal file
BIN
Docs/dijagrami/communication_components.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
150
Docs/dijagrami/implementacija.drawio
Normal file
150
Docs/dijagrami/implementacija.drawio
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
<mxfile host="Electron" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.5.2 Chrome/142.0.7444.265 Electron/39.6.1 Safari/537.36" version="29.5.2">
|
||||||
|
<diagram name="Page-1" id="IZF_J8x8XDGuG-dahjO2">
|
||||||
|
<mxGraphModel dx="1040" dy="620" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0" />
|
||||||
|
<mxCell id="1" parent="0" />
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-66" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1">
|
||||||
|
<mxGeometry height="560" width="650" x="60" y="40" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-65" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" value="" vertex="1">
|
||||||
|
<mxGeometry height="480" width="650" x="60" y="600" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-1" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=40;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="<i>&lt;&lt;interface&gt;&gt;</i><br><div>IMessagePublisher</div>" vertex="1">
|
||||||
|
<mxGeometry height="66" width="210" x="144" y="680" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-2" parent="KJVqxsW90z-SvCUaH26X-1" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="+ Publish(message: IMesasge)" vertex="1">
|
||||||
|
<mxGeometry height="26" width="210" y="40" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-10" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=40;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="<i>&lt;&lt;interface&gt;&gt;</i><div>IMessageSubscriber</div>" vertex="1">
|
||||||
|
<mxGeometry height="66" width="250" x="399" y="680" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-11" parent="KJVqxsW90z-SvCUaH26X-10" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="+ Subscribe(handler: Func&lt;IMessage&gt;)" vertex="1">
|
||||||
|
<mxGeometry height="26" width="250" y="40" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-15" parent="1" style="html=1;whiteSpace=wrap;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="<i>&lt;&lt;interface&gt;&gt;</i><div>IMessage</div>" vertex="1">
|
||||||
|
<mxGeometry height="50" width="110" x="329" y="850" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-20" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-26" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;exitX=0.465;exitY=1.106;exitDx=0;exitDy=0;entryX=0.465;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;exitPerimeter=0;" target="KJVqxsW90z-SvCUaH26X-1" value="">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="210" y="250" as="sourcePoint" />
|
||||||
|
<mxPoint x="490" y="200" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-21" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-2" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.724;exitY=0.971;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;exitPerimeter=0;" target="KJVqxsW90z-SvCUaH26X-15" value="Use">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="230" y="56" as="sourcePoint" />
|
||||||
|
<mxPoint x="390" y="56" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-22" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-11" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.247;exitY=0.9;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;exitPerimeter=0;" target="KJVqxsW90z-SvCUaH26X-15" value="Use">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="480" y="6" as="sourcePoint" />
|
||||||
|
<mxPoint x="640" y="6" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-23" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="RabbitMqPublisher" vertex="1">
|
||||||
|
<mxGeometry height="80" width="230" x="134" y="440" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-24" parent="KJVqxsW90z-SvCUaH26X-23" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="- connection: IRabbitMqConnection" vertex="1">
|
||||||
|
<mxGeometry height="26" width="230" y="26" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-25" parent="KJVqxsW90z-SvCUaH26X-23" style="line;strokeWidth=1;fillColor=#f5f5f5;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=#666666;fontColor=#333333;" value="" vertex="1">
|
||||||
|
<mxGeometry height="2" width="230" y="52" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-26" parent="KJVqxsW90z-SvCUaH26X-23" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="+ Publish(message: IMesasge)" vertex="1">
|
||||||
|
<mxGeometry height="26" width="230" y="54" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-27" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="RabbitMqSubscriber" vertex="1">
|
||||||
|
<mxGeometry height="80" width="250" x="406" y="440" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-28" parent="KJVqxsW90z-SvCUaH26X-27" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="- connection: IRabbitMqConnection" vertex="1">
|
||||||
|
<mxGeometry height="26" width="250" y="26" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-29" parent="KJVqxsW90z-SvCUaH26X-27" style="line;strokeWidth=1;fillColor=#f5f5f5;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=#666666;fontColor=#333333;" value="" vertex="1">
|
||||||
|
<mxGeometry height="2" width="250" y="52" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-30" parent="KJVqxsW90z-SvCUaH26X-27" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="+ Subscribe(handler: Func&lt;IMessage&gt;)" vertex="1">
|
||||||
|
<mxGeometry height="26" width="250" y="54" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-31" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-30" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;exitX=0.471;exitY=0.93;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="KJVqxsW90z-SvCUaH26X-10" value="">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="473" y="290" as="sourcePoint" />
|
||||||
|
<mxPoint x="630" y="390" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-32" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=40;fillColor=#f5f5f5;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontColor=#333333;strokeColor=#666666;" value="<i>&lt;&lt;interface&gt;&gt;</i><div>IRabbitMqConnection</div>" vertex="1">
|
||||||
|
<mxGeometry height="66" width="180" x="294" y="280" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-33" parent="KJVqxsW90z-SvCUaH26X-32" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="+ CreateChannel(): IChannel" vertex="1">
|
||||||
|
<mxGeometry height="26" width="180" y="40" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-36" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-23" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.273;entryY=0.981;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;entryPerimeter=0;" target="KJVqxsW90z-SvCUaH26X-33" value="Use">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="220" y="520" as="sourcePoint" />
|
||||||
|
<mxPoint x="320" y="440" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-37" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-27" style="endArrow=open;endSize=12;dashed=1;html=1;rounded=0;exitX=0.25;exitY=0;exitDx=0;exitDy=0;entryX=0.728;entryY=1.002;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;entryPerimeter=0;" target="KJVqxsW90z-SvCUaH26X-33" value="Use">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="550" y="380" as="sourcePoint" />
|
||||||
|
<mxPoint x="640" y="460" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-38" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="RabbitMqConnection" vertex="1">
|
||||||
|
<mxGeometry height="140" width="230" x="269" y="70" as="geometry">
|
||||||
|
<mxRectangle height="30" width="150" x="255" y="585" as="alternateBounds" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-39" parent="KJVqxsW90z-SvCUaH26X-38" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="- connection: IConnection<div>- configuration: IConfiguration</div>" vertex="1">
|
||||||
|
<mxGeometry height="44" width="230" y="26" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-40" parent="KJVqxsW90z-SvCUaH26X-38" style="line;strokeWidth=1;fillColor=#f5f5f5;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=#666666;fontColor=#333333;" value="" vertex="1">
|
||||||
|
<mxGeometry width="230" y="70" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-41" parent="KJVqxsW90z-SvCUaH26X-38" style="text;strokeColor=#666666;fillColor=#f5f5f5;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontColor=#333333;" value="+ CreateChannel(): IChannel<div>- CreateConnection()</div><div>-&nbsp;CreateConnectionFactory()</div>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="230" y="70" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-49" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-38" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" target="KJVqxsW90z-SvCUaH26X-32" value="">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="370" y="-190" as="sourcePoint" />
|
||||||
|
<mxPoint x="600" y="470" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-51" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="MoveShapeMessage" vertex="1">
|
||||||
|
<mxGeometry height="52" width="140" x="210" y="990" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-52" parent="KJVqxsW90z-SvCUaH26X-51" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="+ ..." vertex="1">
|
||||||
|
<mxGeometry height="26" width="140" y="26" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-60" parent="1" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="AddRectangleMessage" vertex="1">
|
||||||
|
<mxGeometry height="52" width="156" x="414" y="990" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-61" parent="KJVqxsW90z-SvCUaH26X-60" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" value="+ ..." vertex="1">
|
||||||
|
<mxGeometry height="26" width="156" y="26" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-62" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-51" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;" target="KJVqxsW90z-SvCUaH26X-15" value="">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="350" y="936" as="sourcePoint" />
|
||||||
|
<mxPoint x="273.5" y="1026" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-63" edge="1" parent="1" source="KJVqxsW90z-SvCUaH26X-60" style="endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;fillColor=#f5f5f5;strokeColor=#666666;exitX=0.25;exitY=0;exitDx=0;exitDy=0;" target="KJVqxsW90z-SvCUaH26X-15" value="">
|
||||||
|
<mxGeometry relative="1" width="160" as="geometry">
|
||||||
|
<mxPoint x="603" y="76" as="sourcePoint" />
|
||||||
|
<mxPoint x="600" y="-44" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-64" parent="1" style="text;html=1;whiteSpace=wrap;align=center;verticalAlign=middle;rounded=0;" value="<font style="font-size: 22px;">...</font>" vertex="1">
|
||||||
|
<mxGeometry height="30" width="60" x="354" y="996" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-67" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=16;" value="Application Layer" vertex="1">
|
||||||
|
<mxGeometry height="30" width="125" x="321.5" y="600" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="KJVqxsW90z-SvCUaH26X-68" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=16;" value="Infrastructure Layer" vertex="1">
|
||||||
|
<mxGeometry height="30" width="180" x="295" y="570" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
BIN
Docs/dijagrami/implementacija.png
Normal file
BIN
Docs/dijagrami/implementacija.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
17
deploy/.env.example
Normal file
17
deploy/.env.example
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# PostgreSQL (shared VPS instance — create DB/user manually)
|
||||||
|
POSTGRES_DB=aips_db
|
||||||
|
POSTGRES_USER=aips_user
|
||||||
|
POSTGRES_PASSWORD=CHANGE_ME_strong_password_here
|
||||||
|
|
||||||
|
# RabbitMQ
|
||||||
|
RABBITMQ_DEFAULT_USER=aips_rabbit
|
||||||
|
RABBITMQ_DEFAULT_PASS=CHANGE_ME_rabbit_password
|
||||||
|
RABBITMQ_DEFAULT_VHOST=/
|
||||||
|
RABBITMQ_EXCHANGE=aips
|
||||||
|
|
||||||
|
# JWT
|
||||||
|
JWT_ISSUER=AIPS
|
||||||
|
JWT_AUDIENCE=AIPSWebApi
|
||||||
|
JWT_KEY=CHANGE_ME_generate_a_64_char_random_string_here
|
||||||
|
JWT_EXPIRATION_MINUTES=60
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION_DAYS=7
|
||||||
14
deploy/Dockerfile.front
Normal file
14
deploy/Dockerfile.front
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM oven/bun:1 AS build
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY front/package.json front/bun.lock ./
|
||||||
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY front/ .
|
||||||
|
RUN bun run build-only
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
COPY deploy/nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
23
deploy/Dockerfile.rt
Normal file
23
deploy/Dockerfile.rt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY dotnet/dotnet.sln dotnet/dotnet.sln
|
||||||
|
COPY dotnet/AipsCore/AipsCore.csproj dotnet/AipsCore/
|
||||||
|
COPY dotnet/AipsWebApi/AipsWebApi.csproj dotnet/AipsWebApi/
|
||||||
|
COPY dotnet/AipsRT/AipsRT.csproj dotnet/AipsRT/
|
||||||
|
COPY dotnet/AipsWorker/AipsWorker.csproj dotnet/AipsWorker/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet restore dotnet.sln
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY dotnet/ dotnet/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet publish AipsRT/AipsRT.csproj -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["dotnet", "AipsRT.dll"]
|
||||||
23
deploy/Dockerfile.webapi
Normal file
23
deploy/Dockerfile.webapi
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY dotnet/dotnet.sln dotnet/dotnet.sln
|
||||||
|
COPY dotnet/AipsCore/AipsCore.csproj dotnet/AipsCore/
|
||||||
|
COPY dotnet/AipsWebApi/AipsWebApi.csproj dotnet/AipsWebApi/
|
||||||
|
COPY dotnet/AipsRT/AipsRT.csproj dotnet/AipsRT/
|
||||||
|
COPY dotnet/AipsWorker/AipsWorker.csproj dotnet/AipsWorker/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet restore dotnet.sln
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY dotnet/ dotnet/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet publish AipsWebApi/AipsWebApi.csproj -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["dotnet", "AipsWebApi.dll"]
|
||||||
22
deploy/Dockerfile.worker
Normal file
22
deploy/Dockerfile.worker
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY dotnet/dotnet.sln dotnet/dotnet.sln
|
||||||
|
COPY dotnet/AipsCore/AipsCore.csproj dotnet/AipsCore/
|
||||||
|
COPY dotnet/AipsWebApi/AipsWebApi.csproj dotnet/AipsWebApi/
|
||||||
|
COPY dotnet/AipsRT/AipsRT.csproj dotnet/AipsRT/
|
||||||
|
COPY dotnet/AipsWorker/AipsWorker.csproj dotnet/AipsWorker/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet restore dotnet.sln
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY dotnet/ dotnet/
|
||||||
|
|
||||||
|
WORKDIR /src/dotnet
|
||||||
|
RUN dotnet publish AipsWorker/AipsWorker.csproj -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "AipsWorker.dll"]
|
||||||
105
deploy/docker-compose.yml
Normal file
105
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
services:
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3-management
|
||||||
|
container_name: aips-rabbitmq
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
||||||
|
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||||
|
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST}
|
||||||
|
volumes:
|
||||||
|
- rabbitmqdata:/var/lib/rabbitmq
|
||||||
|
ports:
|
||||||
|
- "15672:15672"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
webapi:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.webapi
|
||||||
|
container_name: aips-webapi
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
ASPNETCORE_URLS: "http://+:8080"
|
||||||
|
ASPNETCORE_ENVIRONMENT: "Production"
|
||||||
|
DB_CONN_STRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
||||||
|
RABBITMQ_AMQP_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}"
|
||||||
|
RABBITMQ_EXCHANGE: "${RABBITMQ_EXCHANGE}"
|
||||||
|
JWT_ISSUER: "${JWT_ISSUER}"
|
||||||
|
JWT_AUDIENCE: "${JWT_AUDIENCE}"
|
||||||
|
JWT_KEY: "${JWT_KEY}"
|
||||||
|
JWT_EXPIRATION_MINUTES: "${JWT_EXPIRATION_MINUTES}"
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION_DAYS: "${JWT_REFRESH_TOKEN_EXPIRATION_DAYS}"
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- back_network
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
rt:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.rt
|
||||||
|
container_name: aips-rt
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
ASPNETCORE_URLS: "http://+:8080"
|
||||||
|
ASPNETCORE_ENVIRONMENT: "Production"
|
||||||
|
DB_CONN_STRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
||||||
|
RABBITMQ_AMQP_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}"
|
||||||
|
RABBITMQ_EXCHANGE: "${RABBITMQ_EXCHANGE}"
|
||||||
|
JWT_ISSUER: "${JWT_ISSUER}"
|
||||||
|
JWT_AUDIENCE: "${JWT_AUDIENCE}"
|
||||||
|
JWT_KEY: "${JWT_KEY}"
|
||||||
|
JWT_EXPIRATION_MINUTES: "${JWT_EXPIRATION_MINUTES}"
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRATION_DAYS: "${JWT_REFRESH_TOKEN_EXPIRATION_DAYS}"
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- back_network
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.worker
|
||||||
|
container_name: aips-worker
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
DB_CONN_STRING: "Host=postgres;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
||||||
|
RABBITMQ_AMQP_URI: "amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/${RABBITMQ_DEFAULT_VHOST}"
|
||||||
|
RABBITMQ_EXCHANGE: "${RABBITMQ_EXCHANGE}"
|
||||||
|
JWT_ISSUER: "${JWT_ISSUER}"
|
||||||
|
JWT_AUDIENCE: "${JWT_AUDIENCE}"
|
||||||
|
JWT_KEY: "${JWT_KEY}"
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- back_network
|
||||||
|
depends_on:
|
||||||
|
rabbitmq:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: deploy/Dockerfile.front
|
||||||
|
container_name: aips-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8090:80"
|
||||||
|
depends_on:
|
||||||
|
- webapi
|
||||||
|
- rt
|
||||||
|
|
||||||
|
networks:
|
||||||
|
back_network:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
rabbitmqdata:
|
||||||
45
deploy/nginx/aips-global.conf
Normal file
45
deploy/nginx/aips-global.conf
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name aips.stewki.com;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name aips.stewki.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/aips.stewki.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/aips.stewki.com/privkey.pem;
|
||||||
|
|
||||||
|
client_max_body_size 10M;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://host.docker.internal:8090;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /hubs/ {
|
||||||
|
proxy_pass http://host.docker.internal:8090;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
proxy_read_timeout 86400s;
|
||||||
|
proxy_send_timeout 86400s;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
deploy/nginx/nginx.conf
Normal file
46
deploy/nginx/nginx.conf
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
upstream webapi {
|
||||||
|
server webapi:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream rt {
|
||||||
|
server rt:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 10M;
|
||||||
|
|
||||||
|
# REST API
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://webapi;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SignalR hubs (WebSocket support)
|
||||||
|
location /hubs/ {
|
||||||
|
proxy_pass http://rt;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
proxy_read_timeout 86400s;
|
||||||
|
proxy_send_timeout 86400s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Vue SPA
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
dos-start-back.bat
Normal file
27
dos-start-back.bat
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<# : batch portion
|
||||||
|
@echo off
|
||||||
|
set "SCRIPT_DIR=%~dp0"
|
||||||
|
powershell -ExecutionPolicy Bypass "iex((Get-Content '%~f0' -Raw))"
|
||||||
|
exit /b
|
||||||
|
#>
|
||||||
|
|
||||||
|
Set-Location (Join-Path $env:SCRIPT_DIR "dotnet")
|
||||||
|
|
||||||
|
$jobs = @()
|
||||||
|
$jobs += Start-Job -ScriptBlock { Set-Location $using:PWD; dotnet run --project AipsWebApi 2>&1 | ForEach-Object { "[WebApi] $_" } }
|
||||||
|
$jobs += Start-Job -ScriptBlock { Set-Location $using:PWD; dotnet run --project AipsRT 2>&1 | ForEach-Object { "[RT] $_" } }
|
||||||
|
$jobs += Start-Job -ScriptBlock { Set-Location $using:PWD; dotnet run --project AipsWorker 2>&1 | ForEach-Object { "[Worker] $_" } }
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ($jobs | Where-Object { $_.State -eq 'Running' }) {
|
||||||
|
foreach ($job in $jobs) {
|
||||||
|
Receive-Job -Job $job
|
||||||
|
}
|
||||||
|
Start-Sleep -Milliseconds 200
|
||||||
|
}
|
||||||
|
foreach ($job in $jobs) {
|
||||||
|
Receive-Job -Job $job
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$jobs | Stop-Job -PassThru | Remove-Job
|
||||||
|
}
|
||||||
4
dos-start-front.bat
Normal file
4
dos-start-front.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0front"
|
||||||
|
|
||||||
|
bun dev
|
||||||
4
dos-start-infra.bat
Normal file
4
dos-start-infra.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0docker"
|
||||||
|
|
||||||
|
docker compose -p aips --env-file ..\.env up
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Application\Models\Shape\Command\DeleteShape\" />
|
<Folder Include="Application\Models\Shape\Command\DeleteShape\" />
|
||||||
<Folder Include="Domain\Models\WhiteboardMembership\Validation\" />
|
|
||||||
<Folder Include="Infrastructure\Persistence\Db\Migrations\" />
|
<Folder Include="Infrastructure\Persistence\Db\Migrations\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace AipsCore.Application.Abstract;
|
||||||
|
|
||||||
|
public interface IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
Guid GetWhiteboardId();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
|
||||||
|
public interface IMessageTypesProvider
|
||||||
|
{
|
||||||
|
ICollection<Type> GetAllMessageTypes();
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace AipsCore.Application.Abstract.MessageBroking;
|
|
||||||
|
|
||||||
public enum MessageTag
|
|
||||||
{
|
|
||||||
Worker,
|
|
||||||
RT
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.AcceptUserRequestToJoin;
|
||||||
|
|
||||||
|
public record AcceptUserRequestToJoinMessage(AcceptUserRequestToJoinCommand Command) : IMessage;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.AcceptUserRequestToJoin;
|
||||||
|
|
||||||
|
public class AcceptUserRequestToJoinMessageHandler : IMessageHandler<AcceptUserRequestToJoinMessage>
|
||||||
|
{
|
||||||
|
private readonly IDispatcher _dispatcher;
|
||||||
|
|
||||||
|
public AcceptUserRequestToJoinMessageHandler(IDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(AcceptUserRequestToJoinMessage message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _dispatcher.Execute(message.Command, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateArrow;
|
using AipsCore.Application.Models.Shape.Command.CreateArrow;
|
||||||
|
|
||||||
namespace AipsCore.Application.Common.Message.AddArrow;
|
namespace AipsCore.Application.Common.Message.AddArrow;
|
||||||
|
|
||||||
public record AddArrowMessage(CreateArrowCommand Command) : IMessage;
|
public record AddArrowMessage(CreateArrowCommand Command) : IMessage, IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
public Guid GetWhiteboardId()
|
||||||
|
{
|
||||||
|
return Guid.Parse(Command.WhiteboardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateLine;
|
using AipsCore.Application.Models.Shape.Command.CreateLine;
|
||||||
|
|
||||||
namespace AipsCore.Application.Common.Message.AddLine;
|
namespace AipsCore.Application.Common.Message.AddLine;
|
||||||
|
|
||||||
public record AddLineMessage(CreateLineCommand Command) : IMessage;
|
public record AddLineMessage(CreateLineCommand Command) : IMessage, IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
public Guid GetWhiteboardId()
|
||||||
|
{
|
||||||
|
return Guid.Parse(Command.WhiteboardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateRectangle;
|
using AipsCore.Application.Models.Shape.Command.CreateRectangle;
|
||||||
|
|
||||||
namespace AipsCore.Application.Common.Message.AddRectangle;
|
namespace AipsCore.Application.Common.Message.AddRectangle;
|
||||||
|
|
||||||
public record AddRectangleMessage(CreateRectangleCommand Command) : IMessage;
|
public record AddRectangleMessage(CreateRectangleCommand Command) : IMessage, IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
public Guid GetWhiteboardId()
|
||||||
|
{
|
||||||
|
return Guid.Parse(Command.WhiteboardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
||||||
|
|
||||||
namespace AipsCore.Application.Common.Message.AddTextShape;
|
namespace AipsCore.Application.Common.Message.AddTextShape;
|
||||||
|
|
||||||
public record AddTextShapeMessage(CreateTextShapeCommand Command) : IMessage;
|
public record AddTextShapeMessage(CreateTextShapeCommand Command) : IMessage, IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
public Guid GetWhiteboardId()
|
||||||
|
{
|
||||||
|
return Guid.Parse(Command.WhiteboardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.ErrorMessage;
|
||||||
|
|
||||||
|
public record ErrorMessage(Guid WhiteboardId, ICollection<ValidationError> Errors) : IMessage, IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
public Guid GetWhiteboardId()
|
||||||
|
{
|
||||||
|
return WhiteboardId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.ErrorMessage;
|
||||||
|
|
||||||
|
public class ErrorMessageHandler : IMessageHandler<ErrorMessage>
|
||||||
|
{
|
||||||
|
private readonly IErrorMessageHandleStrategy _handleStrategy;
|
||||||
|
|
||||||
|
public ErrorMessageHandler(IErrorMessageHandleStrategy handleStrategy)
|
||||||
|
{
|
||||||
|
_handleStrategy = handleStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(ErrorMessage message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _handleStrategy.Handle(message, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace AipsCore.Application.Common.Message.ErrorMessage;
|
||||||
|
|
||||||
|
public interface IErrorMessageHandleStrategy
|
||||||
|
{
|
||||||
|
Task Handle(ErrorMessage message, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
||||||
|
|
||||||
namespace AipsCore.Application.Common.Message.MoveShape;
|
namespace AipsCore.Application.Common.Message.MoveShape;
|
||||||
|
|
||||||
public record MoveShapeMessage(MoveShapeCommand Command) : IMessage;
|
public record MoveShapeMessage(Guid WhiteboardId, MoveShapeCommand Command) : IMessage, IWhiteboardAwareContext
|
||||||
|
{
|
||||||
|
public Guid GetWhiteboardId()
|
||||||
|
{
|
||||||
|
return WhiteboardId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.RejectUserRequestToJoin;
|
||||||
|
|
||||||
|
public record RejectUserRequestToJoinMessage(RejectUserRequestToJoinCommand Command): IMessage;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.RejectUserRequestToJoin;
|
||||||
|
|
||||||
|
public class RejectUserRequestToJoinMessageHandler : IMessageHandler<RejectUserRequestToJoinMessage>
|
||||||
|
{
|
||||||
|
private readonly IDispatcher _dispatcher;
|
||||||
|
|
||||||
|
public RejectUserRequestToJoinMessageHandler(IDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(RejectUserRequestToJoinMessage message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _dispatcher.Execute(message.Command, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.UserCanceledRequestToJoin;
|
||||||
|
|
||||||
|
public record UserCanceledRequestToJoinMessage(UserCanceledRequestToJoinCommand Command): IMessage;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Common.Message.UserCanceledRequestToJoin;
|
||||||
|
|
||||||
|
public class UserCanceledRequestToJoinMessageHandler : IMessageHandler<UserCanceledRequestToJoinMessage>
|
||||||
|
{
|
||||||
|
private readonly IDispatcher _dispatcher;
|
||||||
|
|
||||||
|
public UserCanceledRequestToJoinMessageHandler(IDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(UserCanceledRequestToJoinMessage message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await _dispatcher.Execute(message.Command, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||||
|
|
||||||
|
public record AcceptUserRequestToJoinCommand(string WhiteboardId, string UserId): ICommand;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
using AipsCore.Domain.Abstract;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||||
|
|
||||||
|
public class AcceptUserRequestToJoinCommandHandler : ICommandHandler<AcceptUserRequestToJoinCommand>
|
||||||
|
{
|
||||||
|
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public AcceptUserRequestToJoinCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork)
|
||||||
|
{
|
||||||
|
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(AcceptUserRequestToJoinCommand command, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var whiteboardId = new WhiteboardId(command.WhiteboardId);
|
||||||
|
var userId = new UserId(command.UserId);
|
||||||
|
|
||||||
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
|
if (membership is null)
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
membership.UpdateStatus(WhiteboardMembershipStatus.Accepted);
|
||||||
|
|
||||||
|
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||||
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using AipsCore.Application.Abstract.Command;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.AddUserToWhiteboard;
|
|
||||||
|
|
||||||
public record AddUserToWhiteboardCommand(
|
|
||||||
string UserId,
|
|
||||||
string WhiteboardId)
|
|
||||||
: ICommand;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.AddUserToWhiteboard;
|
|
||||||
|
|
||||||
public static class AddUserToWhiteboardCommandErrors
|
|
||||||
{
|
|
||||||
public static ValidationError WhiteboardDoesNotExist(WhiteboardId whiteboardId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_not_exists",
|
|
||||||
Message: $"Whiteboard with id '{whiteboardId}' does not exist.");
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using AipsCore.Application.Abstract;
|
|
||||||
using AipsCore.Application.Abstract.Command;
|
|
||||||
using AipsCore.Domain.Abstract;
|
|
||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.External;
|
|
||||||
using AipsCore.Domain.Models.User.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.External;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.AddUserToWhiteboard;
|
|
||||||
|
|
||||||
public class AddUserToWhiteboardCommandHandler
|
|
||||||
: ICommandHandler<AddUserToWhiteboardCommand>
|
|
||||||
{
|
|
||||||
private readonly IWhiteboardRepository _whiteboardRepository;
|
|
||||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
|
||||||
private readonly IDispatcher _dispatcher;
|
|
||||||
|
|
||||||
public AddUserToWhiteboardCommandHandler(
|
|
||||||
IWhiteboardRepository whiteboardRepository,
|
|
||||||
IWhiteboardMembershipRepository whiteboardMembershipRepository,
|
|
||||||
IUserRepository userRepository,
|
|
||||||
IUnitOfWork unitOfWork,
|
|
||||||
IDispatcher dispatcher)
|
|
||||||
{
|
|
||||||
_whiteboardRepository = whiteboardRepository;
|
|
||||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
|
||||||
_userRepository = userRepository;
|
|
||||||
_unitOfWork = unitOfWork;
|
|
||||||
_dispatcher = dispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Domain.Models.Whiteboard.Whiteboard? _whiteboard;
|
|
||||||
private Domain.Models.User.User? _user;
|
|
||||||
|
|
||||||
public async Task Handle(AddUserToWhiteboardCommand command, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
_whiteboard = await _whiteboardRepository.GetByIdAsync(new WhiteboardId(command.WhiteboardId), cancellationToken);
|
|
||||||
_user = await _userRepository.GetByIdAsync(new UserId(command.UserId), cancellationToken);
|
|
||||||
|
|
||||||
Validate(command);
|
|
||||||
|
|
||||||
await _whiteboard!.AddUserAsync(_user!, _whiteboardMembershipRepository, cancellationToken);
|
|
||||||
|
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Validate(AddUserToWhiteboardCommand command)
|
|
||||||
{
|
|
||||||
if (_whiteboard is null)
|
|
||||||
{
|
|
||||||
throw new ValidationException(WhiteboardErrors.NotFound(new WhiteboardId(command.WhiteboardId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_user is null)
|
|
||||||
{
|
|
||||||
throw new ValidationException(UserErrors.NotFound(new UserId(command.UserId)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard;
|
|
||||||
|
|
||||||
public static class BanUserFromWhiteboardCommandErrors
|
|
||||||
{
|
|
||||||
public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_membership_not_found",
|
|
||||||
Message: $"User with id '{userId.IdValue}' is not a member of whiteboard with id '{whiteboardId.IdValue}'");
|
|
||||||
|
|
||||||
public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_not_found",
|
|
||||||
Message: $"Whiteboard with id '{whiteboardId.IdValue}' not found.");
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,10 @@ using AipsCore.Domain.Abstract;
|
|||||||
using AipsCore.Domain.Common.Validation;
|
using AipsCore.Domain.Common.Validation;
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
using AipsCore.Domain.Models.Whiteboard.External;
|
using AipsCore.Domain.Models.Whiteboard.External;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard;
|
namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard;
|
||||||
|
|
||||||
@@ -37,14 +39,14 @@ public class BanUserFromWhiteboardCommandHandler : ICommandHandler<BanUserFromWh
|
|||||||
|
|
||||||
if (whiteboard is null)
|
if (whiteboard is null)
|
||||||
{
|
{
|
||||||
throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||||
}
|
}
|
||||||
|
|
||||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
if (membership is null)
|
if (membership is null)
|
||||||
{
|
{
|
||||||
throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteboard.BanUser(_userContext.GetCurrentUserId(), membership);
|
whiteboard.BanUser(_userContext.GetCurrentUserId(), membership);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||||
|
|
||||||
|
public record JoinWithCodeCommand(string Code): ICommand<JoinWithCodeDto>;
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
using AipsCore.Application.Abstract.UserContext;
|
||||||
|
using AipsCore.Domain.Abstract;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.External;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||||
|
|
||||||
|
public class JoinWithCodeCommandHandler : ICommandHandler<JoinWithCodeCommand, JoinWithCodeDto>
|
||||||
|
{
|
||||||
|
private readonly IWhiteboardRepository _whiteboardRepository;
|
||||||
|
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly IUserContext _userContext;
|
||||||
|
|
||||||
|
public JoinWithCodeCommandHandler(
|
||||||
|
IWhiteboardRepository whiteboardRepository,
|
||||||
|
IWhiteboardMembershipRepository whiteboardMembershipRepository,
|
||||||
|
IUnitOfWork unitOfWork,
|
||||||
|
IUserContext userContext)
|
||||||
|
{
|
||||||
|
_whiteboardRepository = whiteboardRepository;
|
||||||
|
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
_userContext = userContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<JoinWithCodeDto> Handle(JoinWithCodeCommand command, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var userId = _userContext.GetCurrentUserId();
|
||||||
|
|
||||||
|
var code = new WhiteboardCode(command.Code);
|
||||||
|
var whiteboard = await _whiteboardRepository.GetByCodeAsync(code, cancellationToken);
|
||||||
|
|
||||||
|
if (whiteboard is null)
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardErrors.NotFound(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!whiteboard.ShouldRequestToJoin(userId))
|
||||||
|
{
|
||||||
|
return new JoinWithCodeDto(whiteboard.Id.IdValue, WhiteboardMembershipStatus.Accepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboard.Id, userId, cancellationToken);
|
||||||
|
|
||||||
|
if (membership is null)
|
||||||
|
{
|
||||||
|
membership = whiteboard.RequestJoin(userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
whiteboard.RequestReJoin(membership);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||||
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return new JoinWithCodeDto(whiteboard.Id.IdValue, membership.Status);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||||
|
|
||||||
|
public record JoinWithCodeDto(string WhiteboardId, WhiteboardMembershipStatus Status);
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard;
|
|
||||||
|
|
||||||
public static class KickUserFromWhiteboardCommandErrors
|
|
||||||
{
|
|
||||||
public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_membership_not_found",
|
|
||||||
Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'");
|
|
||||||
|
|
||||||
public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_not_found",
|
|
||||||
Message: $"Whiteboard with id '{whiteboardId}' not found.");
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,10 @@ using AipsCore.Domain.Abstract;
|
|||||||
using AipsCore.Domain.Common.Validation;
|
using AipsCore.Domain.Common.Validation;
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
using AipsCore.Domain.Models.Whiteboard.External;
|
using AipsCore.Domain.Models.Whiteboard.External;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard;
|
namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard;
|
||||||
|
|
||||||
@@ -37,14 +39,14 @@ public class KickUserFromWhiteboardCommandHandler : ICommandHandler<KickUserFrom
|
|||||||
|
|
||||||
if (whiteboard is null)
|
if (whiteboard is null)
|
||||||
{
|
{
|
||||||
throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||||
}
|
}
|
||||||
|
|
||||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
if (membership is null)
|
if (membership is null)
|
||||||
{
|
{
|
||||||
throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteboard.KickUser(_userContext.GetCurrentUserId(), membership);
|
whiteboard.KickUser(_userContext.GetCurrentUserId(), membership);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||||
|
|
||||||
|
public record RejectUserRequestToJoinCommand(string WhiteboardId, string UserId): ICommand;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
using AipsCore.Domain.Abstract;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||||
|
|
||||||
|
public class RejectUserRequestToJoinCommandHandler : ICommandHandler<RejectUserRequestToJoinCommand>
|
||||||
|
{
|
||||||
|
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public RejectUserRequestToJoinCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork)
|
||||||
|
{
|
||||||
|
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(RejectUserRequestToJoinCommand command, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var whiteboardId = new WhiteboardId(command.WhiteboardId);
|
||||||
|
var userId = new UserId(command.UserId);
|
||||||
|
|
||||||
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
|
if (membership is null)
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
membership.UpdateStatus(WhiteboardMembershipStatus.Rejected);
|
||||||
|
|
||||||
|
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||||
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard;
|
|
||||||
|
|
||||||
public static class UnbanUserFromWhiteboardCommandErrors
|
|
||||||
{
|
|
||||||
public static ValidationError WhiteboardMembershipNotFound(WhiteboardId whiteboardId, UserId userId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_membership_not_found",
|
|
||||||
Message: $"User with id '{userId}' is not a member of whiteboard with id '{whiteboardId}'");
|
|
||||||
|
|
||||||
public static ValidationError WhiteboardNotFound(WhiteboardId whiteboardId)
|
|
||||||
=> new ValidationError(
|
|
||||||
Code: "whiteboard_not_found",
|
|
||||||
Message: $"Whiteboard with id '{whiteboardId}' not found.");
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,10 @@ using AipsCore.Domain.Abstract;
|
|||||||
using AipsCore.Domain.Common.Validation;
|
using AipsCore.Domain.Common.Validation;
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
using AipsCore.Domain.Models.Whiteboard.External;
|
using AipsCore.Domain.Models.Whiteboard.External;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard;
|
namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard;
|
||||||
|
|
||||||
@@ -37,14 +39,14 @@ public class UnbanUserFromWhiteboardCommandHandler : ICommandHandler<UnbanUserFr
|
|||||||
|
|
||||||
if (whiteboard is null)
|
if (whiteboard is null)
|
||||||
{
|
{
|
||||||
throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||||
}
|
}
|
||||||
|
|
||||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
if (membership is null)
|
if (membership is null)
|
||||||
{
|
{
|
||||||
throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteboard.UnbanUser(_userContext.GetCurrentUserId(), membership);
|
whiteboard.UnbanUser(_userContext.GetCurrentUserId(), membership);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
|
|
||||||
|
public record UserCanceledRequestToJoinCommand(string WhiteboardId, string UserId): ICommand;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using AipsCore.Application.Abstract.Command;
|
||||||
|
using AipsCore.Domain.Abstract;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
|
|
||||||
|
public class CancelJoinRequestCommandHandler : ICommandHandler<UserCanceledRequestToJoinCommand>
|
||||||
|
{
|
||||||
|
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public CancelJoinRequestCommandHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository, IUnitOfWork unitOfWork)
|
||||||
|
{
|
||||||
|
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(UserCanceledRequestToJoinCommand command, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var whiteboardId = new WhiteboardId(command.WhiteboardId);
|
||||||
|
var userId = new UserId(command.UserId);
|
||||||
|
|
||||||
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
|
if (membership is null)
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
membership.UpdateStatus(WhiteboardMembershipStatus.Cancelled);
|
||||||
|
|
||||||
|
await _whiteboardMembershipRepository.SaveAsync(membership, cancellationToken);
|
||||||
|
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using AipsCore.Application.Abstract.Query;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
|
||||||
|
|
||||||
|
public record GetMembershipStatusQuery(string WhiteboardId, string UserId): IQuery<WhiteboardMembershipStatus>;
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using AipsCore.Application.Abstract.Query;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
|
namespace AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
|
||||||
|
|
||||||
|
public class GetMembershipStatusQueryHandler : IQueryHandler<GetMembershipStatusQuery, WhiteboardMembershipStatus>
|
||||||
|
{
|
||||||
|
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
||||||
|
|
||||||
|
public GetMembershipStatusQueryHandler(IWhiteboardMembershipRepository whiteboardMembershipRepository)
|
||||||
|
{
|
||||||
|
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WhiteboardMembershipStatus> Handle(GetMembershipStatusQuery query, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var userId = new UserId(query.UserId);
|
||||||
|
var whiteboardId = new WhiteboardId(query.WhiteboardId);
|
||||||
|
|
||||||
|
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||||
|
|
||||||
|
if (membership is null)
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return membership.Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using AipsCore.Application.Abstract.Query;
|
using AipsCore.Application.Abstract.Query;
|
||||||
using AipsCore.Application.Abstract.UserContext;
|
using AipsCore.Application.Abstract.UserContext;
|
||||||
using AipsCore.Domain.Models.Whiteboard.Enums;
|
using AipsCore.Domain.Models.Whiteboard.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
using AipsCore.Infrastructure.Persistence.Db;
|
using AipsCore.Infrastructure.Persistence.Db;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ public class GetRecentWhiteboardsQueryHandler : IQueryHandler<GetRecentWhiteboar
|
|||||||
.Include(m => m.Whiteboard)
|
.Include(m => m.Whiteboard)
|
||||||
.Where(m => (
|
.Where(m => (
|
||||||
m.UserId == userIdGuid &&
|
m.UserId == userIdGuid &&
|
||||||
m.IsBanned == false &&
|
m.Status != WhiteboardMembershipStatus.Banned &&
|
||||||
m.Whiteboard != null &&
|
m.Whiteboard != null &&
|
||||||
m.Whiteboard.State != WhiteboardState.Deleted
|
m.Whiteboard.State != WhiteboardState.Deleted
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class GetWhiteboardInfoRTQueryHandler
|
|||||||
return _context.Whiteboards
|
return _context.Whiteboards
|
||||||
.Where(w => w.Id == whiteboardId)
|
.Where(w => w.Id == whiteboardId)
|
||||||
.Include(w => w.Memberships)
|
.Include(w => w.Memberships)
|
||||||
|
.ThenInclude(m => m.User)
|
||||||
.Include(w => w.Owner)
|
.Include(w => w.Owner)
|
||||||
.Include(w => w.Shapes);
|
.Include(w => w.Shapes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
using AipsCore.Application.Abstract.Command;
|
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
|
||||||
|
|
||||||
public record CreateWhiteboardMembershipCommand(
|
|
||||||
string WhiteboardId,
|
|
||||||
bool IsBanned,
|
|
||||||
bool EditingEnabled,
|
|
||||||
bool CanJoin)
|
|
||||||
: ICommand<WhiteboardMembershipId>;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using AipsCore.Application.Abstract.Command;
|
|
||||||
using AipsCore.Application.Abstract.UserContext;
|
|
||||||
using AipsCore.Domain.Abstract;
|
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
|
||||||
|
|
||||||
public class CreateWhiteboardMembershipCommandHandler : ICommandHandler<CreateWhiteboardMembershipCommand, WhiteboardMembershipId>
|
|
||||||
{
|
|
||||||
private readonly IWhiteboardMembershipRepository _whiteboardMembershipRepository;
|
|
||||||
private readonly IUserContext _userContext;
|
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
|
||||||
|
|
||||||
public CreateWhiteboardMembershipCommandHandler(
|
|
||||||
IWhiteboardMembershipRepository whiteboardMembershipRepository,
|
|
||||||
IUserContext userContext,
|
|
||||||
IUnitOfWork unitOfWork)
|
|
||||||
{
|
|
||||||
_whiteboardMembershipRepository = whiteboardMembershipRepository;
|
|
||||||
_userContext = userContext;
|
|
||||||
_unitOfWork = unitOfWork;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<WhiteboardMembershipId> Handle(CreateWhiteboardMembershipCommand command, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var userId = _userContext.GetCurrentUserId();
|
|
||||||
|
|
||||||
var whiteboardMembership = Domain.Models.WhiteboardMembership.WhiteboardMembership.Create(
|
|
||||||
command.WhiteboardId,
|
|
||||||
userId.IdValue,
|
|
||||||
command.IsBanned,
|
|
||||||
command.EditingEnabled,
|
|
||||||
command.CanJoin,
|
|
||||||
DateTime.UtcNow);
|
|
||||||
|
|
||||||
await _whiteboardMembershipRepository.SaveAsync(whiteboardMembership, cancellationToken);
|
|
||||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return whiteboardMembership.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ public class MaxLengthRule : AbstractRule
|
|||||||
{
|
{
|
||||||
private readonly string _stringValue;
|
private readonly string _stringValue;
|
||||||
private readonly int _maximumLentgh;
|
private readonly int _maximumLentgh;
|
||||||
protected override string ErrorCode => "minimum_length";
|
protected override string ErrorCode => "maximum_length";
|
||||||
protected override string ErrorMessage
|
protected override string ErrorMessage
|
||||||
=> $"Length of '{ValueObjectName}' must be at most {_maximumLentgh} characters";
|
=> $"Length of '{ValueObjectName}' must be at most {_maximumLentgh} characters";
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ namespace AipsCore.Domain.Models.Whiteboard.External;
|
|||||||
public interface IWhiteboardRepository
|
public interface IWhiteboardRepository
|
||||||
: IAbstractRepository<Whiteboard, WhiteboardId>, ISoftDeletableRepository<WhiteboardId>
|
: IAbstractRepository<Whiteboard, WhiteboardId>, ISoftDeletableRepository<WhiteboardId>
|
||||||
{
|
{
|
||||||
Task<bool> WhiteboardCodeExists(WhiteboardCode whiteboardCode);
|
Task<bool> WhiteboardCodeExistsAsync(WhiteboardCode whiteboardCode);
|
||||||
|
Task<Whiteboard?> GetByCodeAsync(WhiteboardCode whiteboardCode, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,38 @@ namespace AipsCore.Domain.Models.Whiteboard.Validation;
|
|||||||
|
|
||||||
public class WhiteboardErrors : AbstractErrors<Whiteboard, WhiteboardId>
|
public class WhiteboardErrors : AbstractErrors<Whiteboard, WhiteboardId>
|
||||||
{
|
{
|
||||||
|
public static ValidationError NotFound(WhiteboardCode whiteboardCode)
|
||||||
|
{
|
||||||
|
const string code = "not_found";
|
||||||
|
string message = $"Whiteboard with code '{whiteboardCode.CodeValue}' was not found!";
|
||||||
|
|
||||||
|
return CreateValidationError(code,message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationError CannotJoin(WhiteboardCode whiteboardCode)
|
||||||
|
{
|
||||||
|
const string code = "cannot_join_whiteboard";
|
||||||
|
string message = $"Cannot join the whiteboard with code '{whiteboardCode.CodeValue}'";
|
||||||
|
|
||||||
|
return CreateValidationError(code,message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationError UserBanned(UserId userId)
|
||||||
|
{
|
||||||
|
const string code = "user_banned_from_whiteboard";
|
||||||
|
string message = $"User with id '{userId}' is banned from this whiteboard.";
|
||||||
|
|
||||||
|
return CreateValidationError(code,message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValidationError UserAlreadyTryingToJoin(UserId userId)
|
||||||
|
{
|
||||||
|
const string code = "user_already_trying_to_join_whiteboard";
|
||||||
|
string message = $"User with id '{userId}' is already trying to join the whiteboard.";
|
||||||
|
|
||||||
|
return CreateValidationError(code,message);
|
||||||
|
}
|
||||||
|
|
||||||
public static ValidationError UserAlreadyAdded(UserId userId)
|
public static ValidationError UserAlreadyAdded(UserId userId)
|
||||||
{
|
{
|
||||||
string code = "user_already_added";
|
string code = "user_already_added";
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public record WhiteboardCode : AbstractValueObject
|
|||||||
{
|
{
|
||||||
whiteboardCode = Generate();
|
whiteboardCode = Generate();
|
||||||
|
|
||||||
codeExists = await whiteboardRepository.WhiteboardCodeExists(whiteboardCode);
|
codeExists = await whiteboardRepository.WhiteboardCodeExistsAsync(whiteboardCode);
|
||||||
} while (codeExists);
|
} while (codeExists);
|
||||||
|
|
||||||
return whiteboardCode;
|
return whiteboardCode;
|
||||||
|
|||||||
@@ -1,42 +1,47 @@
|
|||||||
using System.Runtime.InteropServices.Swift;
|
|
||||||
using AipsCore.Domain.Abstract;
|
|
||||||
using AipsCore.Domain.Common.Validation;
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
using AipsCore.Domain.Models.Whiteboard.Enums;
|
using AipsCore.Domain.Models.Whiteboard.Enums;
|
||||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.Whiteboard;
|
namespace AipsCore.Domain.Models.Whiteboard;
|
||||||
|
|
||||||
public partial class Whiteboard : DomainModel<WhiteboardId>
|
public partial class Whiteboard
|
||||||
{
|
{
|
||||||
public async Task AddUserAsync(
|
public WhiteboardMembership.WhiteboardMembership RequestJoin(UserId userId)
|
||||||
User.User user,
|
|
||||||
IWhiteboardMembershipRepository membershipRepository,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
{
|
||||||
var membership
|
return WhiteboardMembership.WhiteboardMembership.Create(
|
||||||
= await membershipRepository.GetByWhiteboardAndUserAsync(this.Id, user.Id, cancellationToken);
|
Id.IdValue,
|
||||||
|
userId.IdValue,
|
||||||
|
false,
|
||||||
|
DetermineJoinStatus(),
|
||||||
|
DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
|
||||||
if (membership is not null)
|
public void RequestReJoin(WhiteboardMembership.WhiteboardMembership membership)
|
||||||
|
{
|
||||||
|
switch (membership.Status)
|
||||||
{
|
{
|
||||||
throw new ValidationException(WhiteboardErrors.UserAlreadyAdded(user.Id));
|
case WhiteboardMembershipStatus.Banned:
|
||||||
|
throw new ValidationException(WhiteboardErrors.UserBanned(membership.UserId));
|
||||||
|
case WhiteboardMembershipStatus.Pending:
|
||||||
|
throw new ValidationException(WhiteboardErrors.UserAlreadyTryingToJoin(membership.UserId));
|
||||||
|
case WhiteboardMembershipStatus.Accepted:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
membership.UpdateStatus(DetermineJoinStatus());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
membership = WhiteboardMembership.WhiteboardMembership.Create(
|
|
||||||
this.Id.IdValue,
|
|
||||||
user.Id.IdValue,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
this.GetCanJoin(),
|
|
||||||
DateTime.UtcNow
|
|
||||||
);
|
|
||||||
|
|
||||||
await membershipRepository.AddAsync(membership, cancellationToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetCanJoin()
|
private WhiteboardMembershipStatus DetermineJoinStatus()
|
||||||
{
|
{
|
||||||
return this.JoinPolicy == WhiteboardJoinPolicy.FreeToJoin;
|
return JoinPolicy switch
|
||||||
|
{
|
||||||
|
WhiteboardJoinPolicy.FreeToJoin => WhiteboardMembershipStatus.Accepted,
|
||||||
|
WhiteboardJoinPolicy.RequestToJoin => WhiteboardMembershipStatus.Pending,
|
||||||
|
WhiteboardJoinPolicy.Private => throw new ValidationException(WhiteboardErrors.CannotJoin(Code)),
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.Whiteboard;
|
|
||||||
|
|
||||||
public partial class Whiteboard
|
|
||||||
{
|
|
||||||
public void BanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
|
||||||
{
|
|
||||||
if (WhiteboardOwnerId != currentUserId)
|
|
||||||
{
|
|
||||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanBanOtherUsers(currentUserId));
|
|
||||||
}
|
|
||||||
|
|
||||||
whiteboardMembership.Ban();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,11 @@ public partial class Whiteboard
|
|||||||
{
|
{
|
||||||
public bool CanUserDelete(UserId userId)
|
public bool CanUserDelete(UserId userId)
|
||||||
{
|
{
|
||||||
return WhiteboardOwnerId.IdValue == userId.IdValue;
|
return IsOwner(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldRequestToJoin(UserId userId)
|
||||||
|
{
|
||||||
|
return !IsOwner(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.Whiteboard;
|
|
||||||
|
|
||||||
public partial class Whiteboard
|
|
||||||
{
|
|
||||||
public void KickUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
|
||||||
{
|
|
||||||
if (WhiteboardOwnerId != currentUserId)
|
|
||||||
{
|
|
||||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanKickOtherUsers(currentUserId));
|
|
||||||
}
|
|
||||||
|
|
||||||
whiteboardMembership.Kick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||||
|
|
||||||
|
namespace AipsCore.Domain.Models.Whiteboard;
|
||||||
|
|
||||||
|
public partial class Whiteboard
|
||||||
|
{
|
||||||
|
public void BanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||||
|
{
|
||||||
|
if (IsOwner(currentUserId))
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanBanOtherUsers(currentUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
whiteboardMembership.Ban();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnbanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||||
|
{
|
||||||
|
if (IsOwner(currentUserId))
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanUnbanOtherUsers(currentUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
whiteboardMembership.Unban();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void KickUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
||||||
|
{
|
||||||
|
if (IsOwner(currentUserId))
|
||||||
|
{
|
||||||
|
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanKickOtherUsers(currentUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
whiteboardMembership.Kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsOwner(UserId userId)
|
||||||
|
{
|
||||||
|
return WhiteboardOwnerId.IdValue == userId.IdValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using AipsCore.Domain.Common.Validation;
|
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
|
||||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.Whiteboard;
|
|
||||||
|
|
||||||
public partial class Whiteboard
|
|
||||||
{
|
|
||||||
public void UnbanUser(UserId currentUserId, WhiteboardMembership.WhiteboardMembership whiteboardMembership)
|
|
||||||
{
|
|
||||||
if (WhiteboardOwnerId != currentUserId)
|
|
||||||
{
|
|
||||||
throw new ValidationException(WhiteboardErrors.OnlyOwnerCanUnbanOtherUsers(currentUserId));
|
|
||||||
}
|
|
||||||
|
|
||||||
whiteboardMembership.Unban();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
|
||||||
|
public enum WhiteboardMembershipStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Accepted,
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
Rejected,
|
||||||
|
Cancelled,
|
||||||
|
Kicked,
|
||||||
|
Banned
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using AipsCore.Domain.Abstract.Validation;
|
||||||
|
using AipsCore.Domain.Common.Validation;
|
||||||
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||||
|
|
||||||
|
namespace AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||||
|
|
||||||
|
public class WhiteboardMembershipErrors : AbstractErrors<WhiteboardMembership, WhiteboardMembershipId>
|
||||||
|
{
|
||||||
|
public static ValidationError NotFound(WhiteboardId whiteboardId, UserId userId)
|
||||||
|
{
|
||||||
|
const string code = "whiteboard_membership_not_found";
|
||||||
|
string message = $"Whiteboard membership with whiteboard id {whiteboardId.IdValue} and user id {userId.IdValue} not found.";
|
||||||
|
|
||||||
|
return CreateValidationError(code, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using AipsCore.Domain.Abstract.Rule;
|
|
||||||
using AipsCore.Domain.Abstract.ValueObject;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
public record WhiteboardMembershipCanJoin : AbstractValueObject
|
|
||||||
{
|
|
||||||
public bool CanJoinValue { get; init; }
|
|
||||||
|
|
||||||
public WhiteboardMembershipCanJoin(bool CanJoinValue)
|
|
||||||
{
|
|
||||||
this.CanJoinValue = CanJoinValue;
|
|
||||||
Validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override ICollection<IRule> GetValidationRules()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using AipsCore.Domain.Abstract.Rule;
|
|
||||||
using AipsCore.Domain.Abstract.ValueObject;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
public record WhiteboardMembershipIsBanned : AbstractValueObject
|
|
||||||
{
|
|
||||||
public bool IsBannedValue { get; init; }
|
|
||||||
|
|
||||||
public WhiteboardMembershipIsBanned(bool IsBannedValue)
|
|
||||||
{
|
|
||||||
this.IsBannedValue = IsBannedValue;
|
|
||||||
Validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override ICollection<IRule> GetValidationRules()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
|
||||||
|
|
||||||
public partial class WhiteboardMembership
|
|
||||||
{
|
|
||||||
public void Ban()
|
|
||||||
{
|
|
||||||
IsBanned = new WhiteboardMembershipIsBanned(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
|
||||||
|
|
||||||
public partial class WhiteboardMembership
|
|
||||||
{
|
|
||||||
public void Kick()
|
|
||||||
{
|
|
||||||
CanJoin = new WhiteboardMembershipCanJoin(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
|
||||||
|
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||||
|
|
||||||
|
public partial class WhiteboardMembership
|
||||||
|
{
|
||||||
|
public void Ban()
|
||||||
|
{
|
||||||
|
Status = WhiteboardMembershipStatus.Banned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unban()
|
||||||
|
{
|
||||||
|
Status = WhiteboardMembershipStatus.Cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Kick()
|
||||||
|
{
|
||||||
|
Status = WhiteboardMembershipStatus.Kicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateStatus(WhiteboardMembershipStatus newStatus)
|
||||||
|
{
|
||||||
|
Status = newStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
|
||||||
|
|
||||||
public partial class WhiteboardMembership
|
|
||||||
{
|
|
||||||
public void Unban()
|
|
||||||
{
|
|
||||||
IsBanned = new WhiteboardMembershipIsBanned(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using AipsCore.Domain.Abstract;
|
using AipsCore.Domain.Abstract;
|
||||||
using AipsCore.Domain.Models.User.ValueObjects;
|
using AipsCore.Domain.Models.User.ValueObjects;
|
||||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||||
|
|
||||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||||
@@ -9,97 +10,84 @@ public partial class WhiteboardMembership : DomainModel<WhiteboardMembershipId>
|
|||||||
{
|
{
|
||||||
public WhiteboardId WhiteboardId { get; private set; }
|
public WhiteboardId WhiteboardId { get; private set; }
|
||||||
public UserId UserId { get; private set; }
|
public UserId UserId { get; private set; }
|
||||||
public WhiteboardMembershipIsBanned IsBanned { get; private set; }
|
|
||||||
public WhiteboardMembershipEditingEnabled EditingEnabled { get; private set; }
|
public WhiteboardMembershipEditingEnabled EditingEnabled { get; private set; }
|
||||||
public WhiteboardMembershipCanJoin CanJoin { get; private set; }
|
public WhiteboardMembershipStatus Status { get; private set; }
|
||||||
public WhiteboardMembershipLastInteractedAt LastInteractedAt { get; private set; }
|
public WhiteboardMembershipLastInteractedAt LastInteractedAt { get; private set; }
|
||||||
|
|
||||||
public WhiteboardMembership(
|
public WhiteboardMembership(
|
||||||
WhiteboardMembershipId id,
|
WhiteboardMembershipId id,
|
||||||
Whiteboard.Whiteboard owner,
|
Whiteboard.Whiteboard whiteboard,
|
||||||
User.User user,
|
User.User user,
|
||||||
WhiteboardMembershipIsBanned isBanned,
|
|
||||||
WhiteboardMembershipEditingEnabled editingEnabled,
|
WhiteboardMembershipEditingEnabled editingEnabled,
|
||||||
WhiteboardMembershipCanJoin canJoin,
|
WhiteboardMembershipStatus status,
|
||||||
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
||||||
: base(id)
|
: base(id)
|
||||||
{
|
{
|
||||||
WhiteboardId = owner.Id;
|
WhiteboardId = whiteboard.Id;
|
||||||
UserId = user.Id;
|
UserId = user.Id;
|
||||||
IsBanned = isBanned;
|
|
||||||
EditingEnabled = editingEnabled;
|
EditingEnabled = editingEnabled;
|
||||||
CanJoin = canJoin;
|
Status = status;
|
||||||
LastInteractedAt = lastInteractedAt;
|
LastInteractedAt = lastInteractedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WhiteboardMembership(
|
public WhiteboardMembership(
|
||||||
WhiteboardMembershipId id,
|
WhiteboardMembershipId id,
|
||||||
WhiteboardId ownerId,
|
WhiteboardId whiteboardId,
|
||||||
UserId userId,
|
UserId userId,
|
||||||
WhiteboardMembershipIsBanned isBanned,
|
|
||||||
WhiteboardMembershipEditingEnabled editingEnabled,
|
WhiteboardMembershipEditingEnabled editingEnabled,
|
||||||
WhiteboardMembershipCanJoin canJoin,
|
WhiteboardMembershipStatus status,
|
||||||
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
||||||
: base(id)
|
: base(id)
|
||||||
{
|
{
|
||||||
WhiteboardId = ownerId;
|
WhiteboardId = whiteboardId;
|
||||||
UserId = userId;
|
UserId = userId;
|
||||||
IsBanned = isBanned;
|
|
||||||
EditingEnabled = editingEnabled;
|
EditingEnabled = editingEnabled;
|
||||||
CanJoin = canJoin;
|
Status = status;
|
||||||
LastInteractedAt = lastInteractedAt;
|
LastInteractedAt = lastInteractedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WhiteboardMembership Create(
|
public static WhiteboardMembership Create(
|
||||||
string id,
|
string id,
|
||||||
string ownerId,
|
string whiteboardId,
|
||||||
string userId,
|
string userId,
|
||||||
bool isBanned,
|
|
||||||
bool editingEnabled,
|
bool editingEnabled,
|
||||||
bool canJoin,
|
WhiteboardMembershipStatus status,
|
||||||
DateTime lastInteractedAt)
|
DateTime lastInteractedAt)
|
||||||
{
|
{
|
||||||
var whiteboardMembershipId = new WhiteboardMembershipId(id);
|
var whiteboardMembershipId = new WhiteboardMembershipId(id);
|
||||||
var whiteboardId = new WhiteboardId(ownerId);
|
var whiteboardIdVo = new WhiteboardId(whiteboardId);
|
||||||
var userIdVo = new UserId(userId);
|
var userIdVo = new UserId(userId);
|
||||||
var isBannedVo = new WhiteboardMembershipIsBanned(isBanned);
|
|
||||||
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
||||||
var canJoinVo = new WhiteboardMembershipCanJoin(canJoin);
|
|
||||||
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
||||||
|
|
||||||
return new WhiteboardMembership(
|
return new WhiteboardMembership(
|
||||||
whiteboardMembershipId,
|
whiteboardMembershipId,
|
||||||
whiteboardId,
|
whiteboardIdVo,
|
||||||
userIdVo,
|
userIdVo,
|
||||||
isBannedVo,
|
|
||||||
editingEnabledVo,
|
editingEnabledVo,
|
||||||
canJoinVo,
|
status,
|
||||||
lastInteractedAtVo);
|
lastInteractedAtVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WhiteboardMembership Create(
|
public static WhiteboardMembership Create(
|
||||||
string ownerId,
|
string whiteboardId,
|
||||||
string userId,
|
string userId,
|
||||||
bool isBanned,
|
|
||||||
bool editingEnabled,
|
bool editingEnabled,
|
||||||
bool canJoin,
|
WhiteboardMembershipStatus status,
|
||||||
DateTime lastInteractedAt)
|
DateTime lastInteractedAt)
|
||||||
{
|
{
|
||||||
var whiteboardMembershipId = WhiteboardMembershipId.Any();
|
var whiteboardMembershipId = WhiteboardMembershipId.Any();
|
||||||
var whiteboardId = new WhiteboardId(ownerId);
|
var whiteboardIdVo = new WhiteboardId(whiteboardId);
|
||||||
var userIdVo = new UserId(userId);
|
var userIdVo = new UserId(userId);
|
||||||
var isBannedVo = new WhiteboardMembershipIsBanned(isBanned);
|
|
||||||
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
||||||
var canJoinVo = new WhiteboardMembershipCanJoin(canJoin);
|
|
||||||
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
||||||
|
|
||||||
return new WhiteboardMembership(
|
return new WhiteboardMembership(
|
||||||
whiteboardMembershipId,
|
whiteboardMembershipId,
|
||||||
whiteboardId,
|
whiteboardIdVo,
|
||||||
userIdVo,
|
userIdVo,
|
||||||
isBannedVo,
|
|
||||||
editingEnabledVo,
|
editingEnabledVo,
|
||||||
canJoinVo,
|
status,
|
||||||
lastInteractedAtVo);
|
lastInteractedAtVo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,10 @@ namespace AipsCore.Infrastructure.Authentication.AuthService;
|
|||||||
|
|
||||||
public class EfAuthService : IAuthService
|
public class EfAuthService : IAuthService
|
||||||
{
|
{
|
||||||
private readonly AipsDbContext _dbContext;
|
|
||||||
private readonly UserManager<Persistence.User.User> _userManager;
|
private readonly UserManager<Persistence.User.User> _userManager;
|
||||||
|
|
||||||
public EfAuthService(AipsDbContext dbContext, UserManager<Persistence.User.User> userManager)
|
public EfAuthService(UserManager<Persistence.User.User> userManager)
|
||||||
{
|
{
|
||||||
_dbContext = dbContext;
|
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,8 +25,8 @@ public class EfAuthService : IAuthService
|
|||||||
|
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
var validationErrors = result.Errors.Select(e => new ValidationError(e.Code, e.Description)).ToList();
|
||||||
throw new Exception($"User registration failed: {errors}");
|
throw new ValidationException(validationErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userManager.AddToRoleAsync(entity, UserRole.User.Name);
|
await _userManager.AddToRoleAsync(entity, UserRole.User.Name);
|
||||||
|
|||||||
503
dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260226153018_ReworkedMembership.Designer.cs
generated
Normal file
503
dotnet/AipsCore/Infrastructure/Persistence/Db/Migrations/20260226153018_ReworkedMembership.Designer.cs
generated
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using AipsCore.Infrastructure.Persistence.Db;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AipsDbContext))]
|
||||||
|
[Migration("20260226153018_ReworkedMembership")]
|
||||||
|
partial class ReworkedMembership
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RefreshTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Shape.Shape", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("AuthorId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Color")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<int?>("EndPositionX")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int?>("EndPositionY")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("PositionX")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("PositionY")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int?>("TextSize")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("TextValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int?>("Thickness")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid>("WhiteboardId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AuthorId");
|
||||||
|
|
||||||
|
b.HasIndex("WhiteboardId");
|
||||||
|
|
||||||
|
b.ToTable("Shapes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.User.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Code")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(8)
|
||||||
|
.HasColumnType("character varying(8)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("JoinPolicy")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("MaxParticipants")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid>("OwnerId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<int>("State")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerId");
|
||||||
|
|
||||||
|
b.ToTable("Whiteboards");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.WhiteboardMembership.WhiteboardMembership", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<bool>("EditingEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastInteractedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("WhiteboardId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.HasIndex("WhiteboardId");
|
||||||
|
|
||||||
|
b.ToTable("WhiteboardMemberships");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Shape.Shape", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "Author")
|
||||||
|
.WithMany("Shapes")
|
||||||
|
.HasForeignKey("AuthorId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", "Whiteboard")
|
||||||
|
.WithMany("Shapes")
|
||||||
|
.HasForeignKey("WhiteboardId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Author");
|
||||||
|
|
||||||
|
b.Navigation("Whiteboard");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "Owner")
|
||||||
|
.WithMany("Whiteboards")
|
||||||
|
.HasForeignKey("OwnerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Owner");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.WhiteboardMembership.WhiteboardMembership", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
||||||
|
.WithMany("Memberships")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", "Whiteboard")
|
||||||
|
.WithMany("Memberships")
|
||||||
|
.HasForeignKey("WhiteboardId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
|
||||||
|
b.Navigation("Whiteboard");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.User.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Memberships");
|
||||||
|
|
||||||
|
b.Navigation("Shapes");
|
||||||
|
|
||||||
|
b.Navigation("Whiteboards");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Memberships");
|
||||||
|
|
||||||
|
b.Navigation("Shapes");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ReworkedMembership : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CanJoin",
|
||||||
|
table: "WhiteboardMemberships");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "IsBanned",
|
||||||
|
table: "WhiteboardMemberships");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Status",
|
||||||
|
table: "WhiteboardMemberships",
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Status",
|
||||||
|
table: "WhiteboardMemberships");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "CanJoin",
|
||||||
|
table: "WhiteboardMemberships",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "IsBanned",
|
||||||
|
table: "WhiteboardMemberships",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
|||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Authentication.RefreshToken.RefreshToken", b =>
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -214,18 +214,15 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<bool>("CanJoin")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("EditingEnabled")
|
b.Property<bool>("EditingEnabled")
|
||||||
.HasColumnType("boolean");
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
b.Property<bool>("IsBanned")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastInteractedAt")
|
b.Property<DateTime>("LastInteractedAt")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b.Property<Guid>("UserId")
|
b.Property<Guid>("UserId")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
@@ -371,7 +368,7 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
|||||||
b.ToTable("AspNetUserTokens", (string)null);
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Authentication.RefreshToken.RefreshToken", b =>
|
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
b.HasOne("AipsCore.Infrastructure.Persistence.User.User", "User")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
|
|||||||
@@ -58,11 +58,18 @@ public class WhiteboardRepository
|
|||||||
entity.State = model.State;
|
entity.State = model.State;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> WhiteboardCodeExists(WhiteboardCode whiteboardCode)
|
public async Task<bool> WhiteboardCodeExistsAsync(WhiteboardCode whiteboardCode)
|
||||||
{
|
{
|
||||||
return await Context.Whiteboards.AnyAsync(w => w.Code == whiteboardCode.CodeValue);
|
return await Context.Whiteboards.AnyAsync(w => w.Code == whiteboardCode.CodeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Domain.Models.Whiteboard.Whiteboard?> GetByCodeAsync(WhiteboardCode whiteboardCode, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var entity = await Context.Whiteboards.FirstOrDefaultAsync(w => w.Code == whiteboardCode.CodeValue, cancellationToken);
|
||||||
|
|
||||||
|
return entity != null ? MapToModel(entity) : null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SoftDeleteAsync(WhiteboardId id, CancellationToken cancellationToken = default)
|
public async Task SoftDeleteAsync(WhiteboardId id, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var entity = await Context.Whiteboards.FindAsync([new Guid(id.IdValue)], cancellationToken);
|
var entity = await Context.Whiteboards.FindAsync([new Guid(id.IdValue)], cancellationToken);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
|
||||||
namespace AipsCore.Infrastructure.Persistence.WhiteboardMembership;
|
namespace AipsCore.Infrastructure.Persistence.WhiteboardMembership;
|
||||||
|
|
||||||
@@ -15,11 +16,9 @@ public class WhiteboardMembership
|
|||||||
|
|
||||||
public User.User? User { get; set; } = null!;
|
public User.User? User { get; set; } = null!;
|
||||||
|
|
||||||
public bool IsBanned { get; set; }
|
|
||||||
|
|
||||||
public bool EditingEnabled { get; set; }
|
public bool EditingEnabled { get; set; }
|
||||||
|
|
||||||
public bool CanJoin { get; set; }
|
public WhiteboardMembershipStatus Status { get; set; }
|
||||||
|
|
||||||
public DateTime LastInteractedAt { get; set; }
|
public DateTime LastInteractedAt { get; set; }
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,8 @@ public class WhiteboardMembershipRepository
|
|||||||
entity.Id.ToString(),
|
entity.Id.ToString(),
|
||||||
entity.WhiteboardId.ToString(),
|
entity.WhiteboardId.ToString(),
|
||||||
entity.UserId.ToString(),
|
entity.UserId.ToString(),
|
||||||
entity.IsBanned,
|
|
||||||
entity.EditingEnabled,
|
entity.EditingEnabled,
|
||||||
entity.CanJoin,
|
entity.Status,
|
||||||
entity.LastInteractedAt
|
entity.LastInteractedAt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -38,18 +37,16 @@ public class WhiteboardMembershipRepository
|
|||||||
Id = new Guid(model.Id.IdValue),
|
Id = new Guid(model.Id.IdValue),
|
||||||
WhiteboardId = new Guid(model.WhiteboardId.IdValue),
|
WhiteboardId = new Guid(model.WhiteboardId.IdValue),
|
||||||
UserId = new Guid(model.UserId.IdValue),
|
UserId = new Guid(model.UserId.IdValue),
|
||||||
IsBanned = model.IsBanned.IsBannedValue,
|
|
||||||
EditingEnabled = model.EditingEnabled.EditingEnabledValue,
|
EditingEnabled = model.EditingEnabled.EditingEnabledValue,
|
||||||
CanJoin = model.CanJoin.CanJoinValue,
|
Status = model.Status,
|
||||||
LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue
|
LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateEntity(WhiteboardMembership entity, Domain.Models.WhiteboardMembership.WhiteboardMembership model)
|
protected override void UpdateEntity(WhiteboardMembership entity, Domain.Models.WhiteboardMembership.WhiteboardMembership model)
|
||||||
{
|
{
|
||||||
entity.IsBanned = model.IsBanned.IsBannedValue;
|
|
||||||
entity.EditingEnabled = model.EditingEnabled.EditingEnabledValue;
|
entity.EditingEnabled = model.EditingEnabled.EditingEnabledValue;
|
||||||
entity.CanJoin = model.CanJoin.CanJoinValue;
|
entity.Status = model.Status;
|
||||||
entity.LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue;
|
entity.LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Common.Message.MoveShape;
|
|
||||||
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
using AipsRT.Model.Memberships;
|
||||||
|
using AipsRT.Model.Users;
|
||||||
using AipsRT.Model.Whiteboard;
|
using AipsRT.Model.Whiteboard;
|
||||||
using AipsRT.Model.Whiteboard.Shapes;
|
using AipsRT.Model.Whiteboard.Shapes;
|
||||||
using AipsRT.Model.Whiteboard.Structs;
|
using AipsRT.Model.Whiteboard.Structs;
|
||||||
@@ -15,33 +21,147 @@ public class WhiteboardHub : Hub
|
|||||||
{
|
{
|
||||||
private readonly WhiteboardManager _whiteboardManager;
|
private readonly WhiteboardManager _whiteboardManager;
|
||||||
private readonly IMessagingService _messagingService;
|
private readonly IMessagingService _messagingService;
|
||||||
|
private readonly MembershipService _membershipService;
|
||||||
|
private readonly UserService _userService;
|
||||||
|
|
||||||
public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService)
|
public WhiteboardHub(WhiteboardManager whiteboardManager, IMessagingService messagingService, MembershipService membershipService, UserService userService)
|
||||||
{
|
{
|
||||||
_whiteboardManager = whiteboardManager;
|
_whiteboardManager = whiteboardManager;
|
||||||
_messagingService = messagingService;
|
_messagingService = messagingService;
|
||||||
|
_membershipService = membershipService;
|
||||||
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||||
|
{
|
||||||
|
var userId = CurrentUserId;
|
||||||
|
var whiteboard = _whiteboardManager.GetWhiteboardForUser(userId);
|
||||||
|
|
||||||
|
if (whiteboard != null)
|
||||||
|
{
|
||||||
|
whiteboard.RemoveActiveUser(userId);
|
||||||
|
_whiteboardManager.RemoveUserFromWhiteboard(userId);
|
||||||
|
|
||||||
|
await Clients.Group(whiteboard.WhiteboardId.ToString())
|
||||||
|
.SendAsync("Leaved", userId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.OnDisconnectedAsync(exception);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task JoinWhiteboard(Guid whiteboardId)
|
public async Task JoinWhiteboard(Guid whiteboardId)
|
||||||
{
|
{
|
||||||
if (!_whiteboardManager.WhiteboardExists(whiteboardId))
|
if (!_whiteboardManager.WhiteboardExists(whiteboardId))
|
||||||
await _whiteboardManager.AddWhiteboard(whiteboardId);
|
{
|
||||||
|
await _whiteboardManager.LoadWhiteboard(whiteboardId);
|
||||||
|
}
|
||||||
|
|
||||||
await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString());
|
await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString());
|
||||||
|
|
||||||
|
var whiteboard = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
||||||
|
|
||||||
var state = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
var userId = CurrentUserId;
|
||||||
|
var ownerId = whiteboard.OwnerId;
|
||||||
|
|
||||||
_whiteboardManager.AddUserToWhiteboard(Guid.Parse(Context.UserIdentifier!), whiteboardId);
|
WhiteboardMembershipStatus status;
|
||||||
|
|
||||||
|
if (userId == ownerId)
|
||||||
|
{
|
||||||
|
status = WhiteboardMembershipStatus.Accepted;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = await _membershipService.GetMembershipStatus(whiteboardId, userId);
|
||||||
|
}
|
||||||
|
|
||||||
await Clients.Caller.SendAsync("InitWhiteboard", state);
|
_whiteboardManager.AddUserToWhiteboard(userId, whiteboardId);
|
||||||
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId)
|
|
||||||
.SendAsync("Joined", Context.UserIdentifier!);
|
if (status == WhiteboardMembershipStatus.Accepted)
|
||||||
|
{
|
||||||
|
|
||||||
|
var joiningUser = whiteboard.Users.FirstOrDefault(u => u.UserId == userId);
|
||||||
|
if (joiningUser == null)
|
||||||
|
{
|
||||||
|
if (ownerId == userId)
|
||||||
|
{
|
||||||
|
joiningUser = whiteboard.Owner;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
joiningUser = new User(userId, Context.User?.Identity?.Name ?? "Unknown", "");
|
||||||
|
whiteboard.AddUser(joiningUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
whiteboard.AddActiveUser(joiningUser);
|
||||||
|
|
||||||
|
var state = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
||||||
|
await Clients.Caller.SendAsync("InitWhiteboard", state);
|
||||||
|
|
||||||
|
await Clients.GroupExcept(whiteboardId.ToString(),
|
||||||
|
Context.ConnectionId).SendAsync("Joined", joiningUser);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Clients.Caller.SendAsync("WaitingForApproval", userId.ToString());
|
||||||
|
|
||||||
|
var user = whiteboard.Users.FirstOrDefault(u => u.UserId == userId);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
user = await _userService.GetUser(userId);
|
||||||
|
whiteboard.AddUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Clients.User(ownerId.ToString()).SendAsync("UserWaitingForApproval", user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AcceptUser(Guid targetUserId)
|
||||||
|
{
|
||||||
|
var whiteboard = CurrentWhiteboard;
|
||||||
|
|
||||||
|
await _messagingService.AcceptedUser(new AcceptUserRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), targetUserId.ToString()));
|
||||||
|
|
||||||
|
var user = whiteboard.Users.FirstOrDefault(u => u.UserId == targetUserId);
|
||||||
|
|
||||||
|
whiteboard.AddActiveUser(user!);
|
||||||
|
|
||||||
|
await Clients.User(targetUserId.ToString()).SendAsync("Accepted");
|
||||||
|
await Clients.User(targetUserId.ToString()).SendAsync("InitWhiteboard", whiteboard);
|
||||||
|
|
||||||
|
await Clients.Group(whiteboard.WhiteboardId.ToString()).SendAsync("Joined", user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RejectUser(Guid targetUserId)
|
||||||
|
{
|
||||||
|
var whiteboard = CurrentWhiteboard;
|
||||||
|
|
||||||
|
await _messagingService.RejectedUser(new RejectUserRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), targetUserId.ToString()));
|
||||||
|
|
||||||
|
await Clients.User(targetUserId.ToString()).SendAsync("Rejected");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CancelJoinRequest()
|
||||||
|
{
|
||||||
|
var userId = CurrentUserId;
|
||||||
|
var whiteboard = _whiteboardManager.GetWhiteboardForUser(userId);
|
||||||
|
|
||||||
|
if (whiteboard != null)
|
||||||
|
{
|
||||||
|
await _messagingService.CancelJoinRequest(new UserCanceledRequestToJoinCommand(whiteboard.WhiteboardId.ToString(), userId.ToString()));
|
||||||
|
await Clients.User(whiteboard.OwnerId.ToString()).SendAsync("UserCanceledJoinRequest", userId.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LeaveWhiteboard(Guid whiteboardId)
|
public async Task LeaveWhiteboard(Guid whiteboardId)
|
||||||
{
|
{
|
||||||
|
var userId = CurrentUserId;
|
||||||
|
_whiteboardManager.RemoveUserFromWhiteboard(userId);
|
||||||
|
_whiteboardManager.GetWhiteboard(whiteboardId)?.RemoveActiveUser(userId);
|
||||||
|
|
||||||
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId)
|
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId)
|
||||||
.SendAsync("Leaved", Context.UserIdentifier!);
|
.SendAsync("Leaved", Context.UserIdentifier!);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Guid CurrentUserId => Guid.Parse(Context.UserIdentifier!);
|
private Guid CurrentUserId => Guid.Parse(Context.UserIdentifier!);
|
||||||
@@ -127,6 +247,6 @@ public class WhiteboardHub : Hub
|
|||||||
{
|
{
|
||||||
await MoveShape(moveShape);
|
await MoveShape(moveShape);
|
||||||
|
|
||||||
await _messagingService.MoveShape(moveShape);
|
await _messagingService.MoveShape(CurrentWhiteboard.WhiteboardId, moveShape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
21
dotnet/AipsRT/Model/Memberships/MembershipService.cs
Normal file
21
dotnet/AipsRT/Model/Memberships/MembershipService.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Query.GetMembershipStatus;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
|
|
||||||
|
namespace AipsRT.Model.Memberships;
|
||||||
|
|
||||||
|
public class MembershipService
|
||||||
|
{
|
||||||
|
private readonly IDispatcher _dispatcher;
|
||||||
|
|
||||||
|
public MembershipService(IDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WhiteboardMembershipStatus> GetMembershipStatus(Guid whiteboardId, Guid userId)
|
||||||
|
{
|
||||||
|
var query = new GetMembershipStatusQuery(whiteboardId.ToString(), userId.ToString());
|
||||||
|
return await _dispatcher.Execute(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
dotnet/AipsRT/Model/Users/User.cs
Normal file
17
dotnet/AipsRT/Model/Users/User.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace AipsRT.Model.Users;
|
||||||
|
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public Guid UserId { get; private set; }
|
||||||
|
|
||||||
|
public string Username { get; private set; }
|
||||||
|
|
||||||
|
public string Email { get; private set; }
|
||||||
|
|
||||||
|
public User(Guid userId, string username, string email)
|
||||||
|
{
|
||||||
|
UserId = userId;
|
||||||
|
Username = username;
|
||||||
|
Email = email;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
dotnet/AipsRT/Model/Users/UserService.cs
Normal file
23
dotnet/AipsRT/Model/Users/UserService.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Models.User.Query.GetUser;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
|
|
||||||
|
namespace AipsRT.Model.Users;
|
||||||
|
|
||||||
|
public class UserService
|
||||||
|
{
|
||||||
|
private readonly IDispatcher _dispatcher;
|
||||||
|
|
||||||
|
public UserService(IDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> GetUser(Guid userId)
|
||||||
|
{
|
||||||
|
var query = new GetUserQuery(userId.ToString());
|
||||||
|
var userQueryDto = await _dispatcher.Execute(query);
|
||||||
|
|
||||||
|
return new User(Guid.Parse(userQueryDto.Id), userQueryDto.UserName, userQueryDto.Email);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
using AipsCore.Application.Abstract;
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardInfoRT;
|
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardInfoRT;
|
||||||
using AipsCore.Domain.Models.Shape.Enums;
|
using AipsCore.Domain.Models.Shape.Enums;
|
||||||
|
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||||
using AipsRT.Model.Whiteboard.Shapes.Map;
|
using AipsRT.Model.Whiteboard.Shapes.Map;
|
||||||
|
using AipsRT.Model.Users;
|
||||||
|
|
||||||
namespace AipsRT.Model.Whiteboard;
|
namespace AipsRT.Model.Whiteboard;
|
||||||
|
|
||||||
@@ -26,8 +28,15 @@ public class GetWhiteboardService
|
|||||||
{
|
{
|
||||||
WhiteboardId = entity.Id,
|
WhiteboardId = entity.Id,
|
||||||
OwnerId = entity.OwnerId,
|
OwnerId = entity.OwnerId,
|
||||||
|
Owner = new User(entity.Owner.Id, entity.Owner.UserName!, entity.Owner.Email!),
|
||||||
|
Code = entity.Code,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
foreach (var membership in entity.Memberships)
|
||||||
|
{
|
||||||
|
whiteboard.AddUser(new User(membership.UserId, membership.User!.UserName!, membership.User.Email!));
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var shape in entity.Shapes)
|
foreach (var shape in entity.Shapes)
|
||||||
{
|
{
|
||||||
switch (shape.Type)
|
switch (shape.Type)
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
using AipsRT.Model.Whiteboard.Shapes;
|
using AipsRT.Model.Whiteboard.Shapes;
|
||||||
|
using AipsRT.Model.Users;
|
||||||
|
|
||||||
namespace AipsRT.Model.Whiteboard;
|
namespace AipsRT.Model.Whiteboard;
|
||||||
|
|
||||||
public class Whiteboard
|
public class Whiteboard
|
||||||
{
|
{
|
||||||
public Guid WhiteboardId { get; set; }
|
public Guid WhiteboardId { get; set; }
|
||||||
|
|
||||||
public Guid OwnerId { get; set; }
|
|
||||||
|
|
||||||
|
public Guid OwnerId { get; set; }
|
||||||
|
public User Owner { get; set; } = null!;
|
||||||
|
|
||||||
|
public string Code {get; set;}
|
||||||
|
|
||||||
|
public List<User> Users { get; } = [];
|
||||||
|
|
||||||
|
public List<User> ActiveUsers { get; } = [];
|
||||||
|
public void AddActiveUser(User user) => ActiveUsers.Add(user);
|
||||||
|
public void RemoveActiveUser(Guid userId) => ActiveUsers.RemoveAll(u => u.UserId == userId);
|
||||||
|
|
||||||
public List<Shape> Shapes { get; } = [];
|
public List<Shape> Shapes { get; } = [];
|
||||||
|
|
||||||
public List<Rectangle> Rectangles { get; } = [];
|
public List<Rectangle> Rectangles { get; } = [];
|
||||||
@@ -20,22 +30,30 @@ public class Whiteboard
|
|||||||
Shapes.Add(shape);
|
Shapes.Add(shape);
|
||||||
Rectangles.Add(shape);
|
Rectangles.Add(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddArrow(Arrow shape)
|
public void AddArrow(Arrow shape)
|
||||||
{
|
{
|
||||||
Shapes.Add(shape);
|
Shapes.Add(shape);
|
||||||
Arrows.Add(shape);
|
Arrows.Add(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddLine(Line shape)
|
public void AddLine(Line shape)
|
||||||
{
|
{
|
||||||
Shapes.Add(shape);
|
Shapes.Add(shape);
|
||||||
Lines.Add(shape);
|
Lines.Add(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTextShape(TextShape shape)
|
public void AddTextShape(TextShape shape)
|
||||||
{
|
{
|
||||||
Shapes.Add(shape);
|
Shapes.Add(shape);
|
||||||
TextShapes.Add(shape);
|
TextShapes.Add(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddUser(User user)
|
||||||
|
{
|
||||||
|
if (!Users.Contains(user))
|
||||||
|
{
|
||||||
|
Users.Add(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ public class WhiteboardManager
|
|||||||
_scopeFactory = scopeFactory;
|
_scopeFactory = scopeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddWhiteboard(Guid whiteboardId)
|
public async Task LoadWhiteboard(Guid whiteboardId)
|
||||||
{
|
{
|
||||||
var getWhiteboardService = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<GetWhiteboardService>();
|
var getWhiteboardService = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<GetWhiteboardService>();
|
||||||
var whiteboard = await getWhiteboardService.GetWhiteboard(whiteboardId);
|
var whiteboard = await getWhiteboardService.GetWhiteboard(whiteboardId);
|
||||||
@@ -47,9 +47,9 @@ public class WhiteboardManager
|
|||||||
return _userInWhiteboards[userId];
|
return _userInWhiteboards[userId];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveUserFromWhiteboard(Guid userId, Guid whiteboardId)
|
public void RemoveUserFromWhiteboard(Guid userId)
|
||||||
{
|
{
|
||||||
_userInWhiteboards.TryRemove(whiteboardId, out _);
|
_userInWhiteboards.TryRemove(userId, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Whiteboard? GetWhiteboardForUser(Guid userId)
|
public Whiteboard? GetWhiteboardForUser(Guid userId)
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
|
using AipsCore.Application.Common.Message.ErrorMessage;
|
||||||
using AipsCore.Infrastructure.DI;
|
using AipsCore.Infrastructure.DI;
|
||||||
using AipsRT.Hubs;
|
using AipsRT.Hubs;
|
||||||
|
using AipsRT.Model.Memberships;
|
||||||
|
using AipsRT.Model.Users;
|
||||||
using AipsRT.Model.Whiteboard;
|
using AipsRT.Model.Whiteboard;
|
||||||
using AipsRT.Services;
|
using AipsRT.Services;
|
||||||
using AipsRT.Services.Interfaces;
|
using AipsRT.Services.Interfaces;
|
||||||
using DotNetEnv;
|
using DotNetEnv;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
Env.Load("../../.env");
|
if (File.Exists("../../.env"))
|
||||||
|
{
|
||||||
|
Env.Load("../../.env");
|
||||||
|
}
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -15,6 +21,13 @@ builder.Configuration.AddEnvironmentVariables();
|
|||||||
builder.Services.AddSignalR();
|
builder.Services.AddSignalR();
|
||||||
|
|
||||||
builder.Services.AddAips(builder.Configuration);
|
builder.Services.AddAips(builder.Configuration);
|
||||||
|
builder.Services.AddAipsMessageHandlers();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<IErrorMessageHandleStrategy, RtErrorHandleStrategy>();
|
||||||
|
builder.Services.AddHostedService<ErrorSubscriberBackgroundService>();
|
||||||
|
|
||||||
|
builder.Services.AddTransient<MembershipService>();
|
||||||
|
builder.Services.AddTransient<UserService>();
|
||||||
|
|
||||||
builder.Services.AddScoped<GetWhiteboardService>();
|
builder.Services.AddScoped<GetWhiteboardService>();
|
||||||
builder.Services.AddSingleton<WhiteboardManager>();
|
builder.Services.AddSingleton<WhiteboardManager>();
|
||||||
|
|||||||
25
dotnet/AipsRT/Services/ErrorSubscriberBackgroundService.cs
Normal file
25
dotnet/AipsRT/Services/ErrorSubscriberBackgroundService.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using AipsCore.Application.Abstract;
|
||||||
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
using AipsCore.Application.Common.Message.ErrorMessage;
|
||||||
|
|
||||||
|
namespace AipsRT.Services;
|
||||||
|
|
||||||
|
public class ErrorSubscriberBackgroundService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly IMessageSubscriber _subscriber;
|
||||||
|
private readonly IDispatcher _dispatcher;
|
||||||
|
|
||||||
|
public ErrorSubscriberBackgroundService(IMessageSubscriber subscriber, IDispatcher dispatcher)
|
||||||
|
{
|
||||||
|
_subscriber = subscriber;
|
||||||
|
_dispatcher = dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
await _subscriber.SubscribeAsync<ErrorMessage>(async (errorMessage, ct) =>
|
||||||
|
{
|
||||||
|
await _dispatcher.Execute(errorMessage, ct);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
||||||
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
using AipsRT.Model.Whiteboard.Shapes;
|
using AipsRT.Model.Whiteboard.Shapes;
|
||||||
|
|
||||||
namespace AipsRT.Services.Interfaces;
|
namespace AipsRT.Services.Interfaces;
|
||||||
@@ -10,6 +13,10 @@ public interface IMessagingService
|
|||||||
Task CreatedArrow(Guid whiteboardId, Arrow arrow);
|
Task CreatedArrow(Guid whiteboardId, Arrow arrow);
|
||||||
Task CreateLine(Guid whiteboardId, Line line);
|
Task CreateLine(Guid whiteboardId, Line line);
|
||||||
Task CreateTextShape(Guid whiteboardId, TextShape textShape);
|
Task CreateTextShape(Guid whiteboardId, TextShape textShape);
|
||||||
|
|
||||||
Task MoveShape(MoveShapeCommand moveShape);
|
Task MoveShape(Guid whiteboardId, MoveShapeCommand moveShape);
|
||||||
|
|
||||||
|
Task AcceptedUser(AcceptUserRequestToJoinCommand command);
|
||||||
|
Task RejectedUser(RejectUserRequestToJoinCommand command);
|
||||||
|
Task CancelJoinRequest(UserCanceledRequestToJoinCommand command);
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
using AipsCore.Application.Abstract.MessageBroking;
|
using AipsCore.Application.Abstract.MessageBroking;
|
||||||
|
using AipsCore.Application.Common.Message.AcceptUserRequestToJoin;
|
||||||
using AipsCore.Application.Common.Message.AddArrow;
|
using AipsCore.Application.Common.Message.AddArrow;
|
||||||
using AipsCore.Application.Common.Message.AddLine;
|
using AipsCore.Application.Common.Message.AddLine;
|
||||||
using AipsCore.Application.Common.Message.AddRectangle;
|
using AipsCore.Application.Common.Message.AddRectangle;
|
||||||
using AipsCore.Application.Common.Message.AddTextShape;
|
using AipsCore.Application.Common.Message.AddTextShape;
|
||||||
using AipsCore.Application.Common.Message.MoveShape;
|
using AipsCore.Application.Common.Message.MoveShape;
|
||||||
|
using AipsCore.Application.Common.Message.RejectUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Common.Message.UserCanceledRequestToJoin;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateArrow;
|
using AipsCore.Application.Models.Shape.Command.CreateArrow;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateLine;
|
using AipsCore.Application.Models.Shape.Command.CreateLine;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateRectangle;
|
using AipsCore.Application.Models.Shape.Command.CreateRectangle;
|
||||||
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
||||||
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
using AipsCore.Application.Models.Shape.Command.MoveShape;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.AcceptUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.RejectUserRequestToJoin;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.UserCanceledRequestToJoin;
|
||||||
using AipsRT.Model.Whiteboard.Shapes;
|
using AipsRT.Model.Whiteboard.Shapes;
|
||||||
using AipsRT.Services.Interfaces;
|
using AipsRT.Services.Interfaces;
|
||||||
|
|
||||||
@@ -95,9 +101,27 @@ public class MessagingService : IMessagingService
|
|||||||
await _messagePublisher.PublishAsync(message);
|
await _messagePublisher.PublishAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveShape(MoveShapeCommand moveShape)
|
public async Task MoveShape(Guid whiteboardId, MoveShapeCommand moveShape)
|
||||||
{
|
{
|
||||||
var message = new MoveShapeMessage(moveShape);
|
var message = new MoveShapeMessage(whiteboardId, moveShape);
|
||||||
await _messagePublisher.PublishAsync(message);
|
await _messagePublisher.PublishAsync(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task AcceptedUser(AcceptUserRequestToJoinCommand command)
|
||||||
|
{
|
||||||
|
var message = new AcceptUserRequestToJoinMessage(command);
|
||||||
|
await _messagePublisher.PublishAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RejectedUser(RejectUserRequestToJoinCommand command)
|
||||||
|
{
|
||||||
|
var message = new RejectUserRequestToJoinMessage(command);
|
||||||
|
await _messagePublisher.PublishAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CancelJoinRequest(UserCanceledRequestToJoinCommand command)
|
||||||
|
{
|
||||||
|
var message = new UserCanceledRequestToJoinMessage(command);
|
||||||
|
await _messagePublisher.PublishAsync(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
36
dotnet/AipsRT/Services/RtErrorHandleStrategy.cs
Normal file
36
dotnet/AipsRT/Services/RtErrorHandleStrategy.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using AipsCore.Application.Common.Message.ErrorMessage;
|
||||||
|
using AipsRT.Hubs;
|
||||||
|
using AipsRT.Model.Whiteboard;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace AipsRT.Services;
|
||||||
|
|
||||||
|
public class RtErrorHandleStrategy : IErrorMessageHandleStrategy
|
||||||
|
{
|
||||||
|
private readonly IHubContext<WhiteboardHub> _hubContext;
|
||||||
|
private readonly WhiteboardManager _whiteboardManager;
|
||||||
|
|
||||||
|
public RtErrorHandleStrategy(IHubContext<WhiteboardHub> hubContext, WhiteboardManager whiteboardManager)
|
||||||
|
{
|
||||||
|
_hubContext = hubContext;
|
||||||
|
_whiteboardManager = whiteboardManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(ErrorMessage message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var activeUsers = _whiteboardManager.GetWhiteboard(message.WhiteboardId)!.ActiveUsers;
|
||||||
|
|
||||||
|
await _whiteboardManager.LoadWhiteboard(message.WhiteboardId);
|
||||||
|
|
||||||
|
var whiteboard = _whiteboardManager.GetWhiteboard(message.WhiteboardId)!;
|
||||||
|
|
||||||
|
foreach (var user in activeUsers)
|
||||||
|
{
|
||||||
|
whiteboard.AddActiveUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hubContext.Clients
|
||||||
|
.Group(whiteboard.WhiteboardId.ToString())
|
||||||
|
.SendAsync("InitWhiteboard", whiteboard, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using AipsCore.Application.Abstract;
|
using AipsCore.Application.Abstract;
|
||||||
using AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard;
|
using AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard;
|
||||||
using AipsCore.Application.Models.Whiteboard.Command.DeleteWhiteboard;
|
using AipsCore.Application.Models.Whiteboard.Command.DeleteWhiteboard;
|
||||||
|
using AipsCore.Application.Models.Whiteboard.Command.JoinWithCode;
|
||||||
using AipsCore.Application.Models.Whiteboard.Query.GetRecentWhiteboards;
|
using AipsCore.Application.Models.Whiteboard.Query.GetRecentWhiteboards;
|
||||||
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboard;
|
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboard;
|
||||||
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardHistory;
|
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardHistory;
|
||||||
using AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Whiteboard = AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard;
|
using Whiteboard = AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard;
|
||||||
@@ -69,7 +69,7 @@ public class WhiteboardController : ControllerBase
|
|||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpPost("join")]
|
[HttpPost("join")]
|
||||||
public async Task<ActionResult> JoinWhiteboard(CreateWhiteboardMembershipCommand command, CancellationToken cancellationToken)
|
public async Task<ActionResult<JoinWithCodeDto>> JoinWhiteboardWithCode(JoinWithCodeCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var result = await _dispatcher.Execute(command, cancellationToken);
|
var result = await _dispatcher.Execute(command, cancellationToken);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ using AipsCore.Infrastructure.Persistence.Db;
|
|||||||
using AipsWebApi.Middleware;
|
using AipsWebApi.Middleware;
|
||||||
using DotNetEnv;
|
using DotNetEnv;
|
||||||
|
|
||||||
Env.Load("../../.env");
|
if (File.Exists("../../.env"))
|
||||||
|
{
|
||||||
|
Env.Load("../../.env");
|
||||||
|
}
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user