Compare commits
49 Commits
main
...
fcb195ddb3
| Author | SHA1 | Date | |
|---|---|---|---|
| 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"]
|
||||
103
deploy/docker-compose.yml
Normal file
103
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,103 @@
|
||||
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
|
||||
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>
|
||||
<Folder Include="Application\Models\Shape\Command\DeleteShape\" />
|
||||
<Folder Include="Domain\Models\WhiteboardMembership\Validation\" />
|
||||
<Folder Include="Infrastructure\Persistence\Db\Migrations\" />
|
||||
</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.Models.Shape.Command.CreateArrow;
|
||||
|
||||
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.Models.Shape.Command.CreateLine;
|
||||
|
||||
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.Models.Shape.Command.CreateRectangle;
|
||||
|
||||
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.Models.Shape.Command.CreateTextShape;
|
||||
|
||||
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.Models.Shape.Command.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.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;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.BanUserFromWhiteboard;
|
||||
|
||||
@@ -37,14 +39,14 @@ public class BanUserFromWhiteboardCommandHandler : ICommandHandler<BanUserFromWh
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(BanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
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.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;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.KickUserFromWhiteboard;
|
||||
|
||||
@@ -37,14 +39,14 @@ public class KickUserFromWhiteboardCommandHandler : ICommandHandler<KickUserFrom
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(KickUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
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.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;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Validation;
|
||||
|
||||
namespace AipsCore.Application.Models.Whiteboard.Command.UnbanUserFromWhiteboard;
|
||||
|
||||
@@ -37,14 +39,14 @@ public class UnbanUserFromWhiteboardCommandHandler : ICommandHandler<UnbanUserFr
|
||||
|
||||
if (whiteboard is null)
|
||||
{
|
||||
throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardNotFound(whiteboardId));
|
||||
throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId));
|
||||
}
|
||||
|
||||
var membership = await _whiteboardMembershipRepository.GetByWhiteboardAndUserAsync(whiteboardId, userId, cancellationToken);
|
||||
|
||||
if (membership is null)
|
||||
{
|
||||
throw new ValidationException(UnbanUserFromWhiteboardCommandErrors.WhiteboardMembershipNotFound(whiteboardId, userId));
|
||||
throw new ValidationException(WhiteboardMembershipErrors.NotFound(whiteboardId, userId));
|
||||
}
|
||||
|
||||
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.UserContext;
|
||||
using AipsCore.Domain.Models.Whiteboard.Enums;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
using AipsCore.Infrastructure.Persistence.Db;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -32,7 +33,7 @@ public class GetRecentWhiteboardsQueryHandler : IQueryHandler<GetRecentWhiteboar
|
||||
.Include(m => m.Whiteboard)
|
||||
.Where(m => (
|
||||
m.UserId == userIdGuid &&
|
||||
m.IsBanned == false &&
|
||||
m.Status != WhiteboardMembershipStatus.Banned &&
|
||||
m.Whiteboard != null &&
|
||||
m.Whiteboard.State != WhiteboardState.Deleted
|
||||
))
|
||||
|
||||
@@ -34,6 +34,7 @@ public class GetWhiteboardInfoRTQueryHandler
|
||||
return _context.Whiteboards
|
||||
.Where(w => w.Id == whiteboardId)
|
||||
.Include(w => w.Memberships)
|
||||
.ThenInclude(m => m.User)
|
||||
.Include(w => w.Owner)
|
||||
.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 int _maximumLentgh;
|
||||
protected override string ErrorCode => "minimum_length";
|
||||
protected override string ErrorCode => "maximum_length";
|
||||
protected override string ErrorMessage
|
||||
=> $"Length of '{ValueObjectName}' must be at most {_maximumLentgh} characters";
|
||||
|
||||
|
||||
@@ -6,5 +6,6 @@ namespace AipsCore.Domain.Models.Whiteboard.External;
|
||||
public interface IWhiteboardRepository
|
||||
: 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 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)
|
||||
{
|
||||
string code = "user_already_added";
|
||||
|
||||
@@ -36,7 +36,7 @@ public record WhiteboardCode : AbstractValueObject
|
||||
{
|
||||
whiteboardCode = Generate();
|
||||
|
||||
codeExists = await whiteboardRepository.WhiteboardCodeExists(whiteboardCode);
|
||||
codeExists = await whiteboardRepository.WhiteboardCodeExistsAsync(whiteboardCode);
|
||||
} while (codeExists);
|
||||
|
||||
return whiteboardCode;
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
using System.Runtime.InteropServices.Swift;
|
||||
using AipsCore.Domain.Abstract;
|
||||
using AipsCore.Domain.Common.Validation;
|
||||
using AipsCore.Domain.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.Enums;
|
||||
using AipsCore.Domain.Models.Whiteboard.Validation;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.External;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Domain.Models.Whiteboard;
|
||||
|
||||
public partial class Whiteboard : DomainModel<WhiteboardId>
|
||||
public partial class Whiteboard
|
||||
{
|
||||
public async Task AddUserAsync(
|
||||
User.User user,
|
||||
IWhiteboardMembershipRepository membershipRepository,
|
||||
CancellationToken cancellationToken = default)
|
||||
public WhiteboardMembership.WhiteboardMembership RequestJoin(UserId userId)
|
||||
{
|
||||
var membership
|
||||
= await membershipRepository.GetByWhiteboardAndUserAsync(this.Id, user.Id, cancellationToken);
|
||||
|
||||
if (membership is not null)
|
||||
{
|
||||
throw new ValidationException(WhiteboardErrors.UserAlreadyAdded(user.Id));
|
||||
}
|
||||
|
||||
membership = WhiteboardMembership.WhiteboardMembership.Create(
|
||||
this.Id.IdValue,
|
||||
user.Id.IdValue,
|
||||
return WhiteboardMembership.WhiteboardMembership.Create(
|
||||
Id.IdValue,
|
||||
userId.IdValue,
|
||||
false,
|
||||
false,
|
||||
this.GetCanJoin(),
|
||||
DateTime.UtcNow
|
||||
);
|
||||
|
||||
await membershipRepository.AddAsync(membership, cancellationToken);
|
||||
DetermineJoinStatus(),
|
||||
DateTime.UtcNow);
|
||||
}
|
||||
|
||||
private bool GetCanJoin()
|
||||
public void RequestReJoin(WhiteboardMembership.WhiteboardMembership membership)
|
||||
{
|
||||
return this.JoinPolicy == WhiteboardJoinPolicy.FreeToJoin;
|
||||
switch (membership.Status)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private WhiteboardMembershipStatus DetermineJoinStatus()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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.Models.User.ValueObjects;
|
||||
using AipsCore.Domain.Models.Whiteboard.ValueObjects;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.ValueObjects;
|
||||
|
||||
namespace AipsCore.Domain.Models.WhiteboardMembership;
|
||||
@@ -9,97 +10,84 @@ public partial class WhiteboardMembership : DomainModel<WhiteboardMembershipId>
|
||||
{
|
||||
public WhiteboardId WhiteboardId { get; private set; }
|
||||
public UserId UserId { get; private set; }
|
||||
public WhiteboardMembershipIsBanned IsBanned { 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 WhiteboardMembership(
|
||||
WhiteboardMembershipId id,
|
||||
Whiteboard.Whiteboard owner,
|
||||
Whiteboard.Whiteboard whiteboard,
|
||||
User.User user,
|
||||
WhiteboardMembershipIsBanned isBanned,
|
||||
WhiteboardMembershipEditingEnabled editingEnabled,
|
||||
WhiteboardMembershipCanJoin canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
||||
: base(id)
|
||||
{
|
||||
WhiteboardId = owner.Id;
|
||||
WhiteboardId = whiteboard.Id;
|
||||
UserId = user.Id;
|
||||
IsBanned = isBanned;
|
||||
EditingEnabled = editingEnabled;
|
||||
CanJoin = canJoin;
|
||||
Status = status;
|
||||
LastInteractedAt = lastInteractedAt;
|
||||
}
|
||||
|
||||
public WhiteboardMembership(
|
||||
WhiteboardMembershipId id,
|
||||
WhiteboardId ownerId,
|
||||
WhiteboardId whiteboardId,
|
||||
UserId userId,
|
||||
WhiteboardMembershipIsBanned isBanned,
|
||||
WhiteboardMembershipEditingEnabled editingEnabled,
|
||||
WhiteboardMembershipCanJoin canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
WhiteboardMembershipLastInteractedAt lastInteractedAt)
|
||||
: base(id)
|
||||
{
|
||||
WhiteboardId = ownerId;
|
||||
WhiteboardId = whiteboardId;
|
||||
UserId = userId;
|
||||
IsBanned = isBanned;
|
||||
EditingEnabled = editingEnabled;
|
||||
CanJoin = canJoin;
|
||||
Status = status;
|
||||
LastInteractedAt = lastInteractedAt;
|
||||
}
|
||||
|
||||
public static WhiteboardMembership Create(
|
||||
string id,
|
||||
string ownerId,
|
||||
string whiteboardId,
|
||||
string userId,
|
||||
bool isBanned,
|
||||
bool editingEnabled,
|
||||
bool canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
DateTime lastInteractedAt)
|
||||
{
|
||||
var whiteboardMembershipId = new WhiteboardMembershipId(id);
|
||||
var whiteboardId = new WhiteboardId(ownerId);
|
||||
var whiteboardIdVo = new WhiteboardId(whiteboardId);
|
||||
var userIdVo = new UserId(userId);
|
||||
var isBannedVo = new WhiteboardMembershipIsBanned(isBanned);
|
||||
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
||||
var canJoinVo = new WhiteboardMembershipCanJoin(canJoin);
|
||||
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
||||
|
||||
return new WhiteboardMembership(
|
||||
whiteboardMembershipId,
|
||||
whiteboardId,
|
||||
whiteboardIdVo,
|
||||
userIdVo,
|
||||
isBannedVo,
|
||||
editingEnabledVo,
|
||||
canJoinVo,
|
||||
status,
|
||||
lastInteractedAtVo);
|
||||
}
|
||||
|
||||
public static WhiteboardMembership Create(
|
||||
string ownerId,
|
||||
string whiteboardId,
|
||||
string userId,
|
||||
bool isBanned,
|
||||
bool editingEnabled,
|
||||
bool canJoin,
|
||||
WhiteboardMembershipStatus status,
|
||||
DateTime lastInteractedAt)
|
||||
{
|
||||
var whiteboardMembershipId = WhiteboardMembershipId.Any();
|
||||
var whiteboardId = new WhiteboardId(ownerId);
|
||||
var whiteboardIdVo = new WhiteboardId(whiteboardId);
|
||||
var userIdVo = new UserId(userId);
|
||||
var isBannedVo = new WhiteboardMembershipIsBanned(isBanned);
|
||||
var editingEnabledVo = new WhiteboardMembershipEditingEnabled(editingEnabled);
|
||||
var canJoinVo = new WhiteboardMembershipCanJoin(canJoin);
|
||||
var lastInteractedAtVo = new WhiteboardMembershipLastInteractedAt(lastInteractedAt);
|
||||
|
||||
return new WhiteboardMembership(
|
||||
whiteboardMembershipId,
|
||||
whiteboardId,
|
||||
whiteboardIdVo,
|
||||
userIdVo,
|
||||
isBannedVo,
|
||||
editingEnabledVo,
|
||||
canJoinVo,
|
||||
status,
|
||||
lastInteractedAtVo);
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,10 @@ namespace AipsCore.Infrastructure.Authentication.AuthService;
|
||||
|
||||
public class EfAuthService : IAuthService
|
||||
{
|
||||
private readonly AipsDbContext _dbContext;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -27,8 +25,8 @@ public class EfAuthService : IAuthService
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
throw new Exception($"User registration failed: {errors}");
|
||||
var validationErrors = result.Errors.Select(e => new ValidationError(e.Code, e.Description)).ToList();
|
||||
throw new ValidationException(validationErrors);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.Authentication.RefreshToken.RefreshToken", b =>
|
||||
modelBuilder.Entity("AipsCore.Infrastructure.Persistence.RefreshToken.RefreshToken", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -214,18 +214,15 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("CanJoin")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("EditingEnabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool>("IsBanned")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("LastInteractedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
@@ -371,7 +368,7 @@ namespace AipsCore.Infrastructure.Persistence.Db.Migrations
|
||||
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")
|
||||
.WithMany()
|
||||
|
||||
@@ -58,11 +58,18 @@ public class WhiteboardRepository
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var entity = await Context.Whiteboards.FindAsync([new Guid(id.IdValue)], cancellationToken);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
|
||||
namespace AipsCore.Infrastructure.Persistence.WhiteboardMembership;
|
||||
|
||||
@@ -15,11 +16,9 @@ public class WhiteboardMembership
|
||||
|
||||
public User.User? User { get; set; } = null!;
|
||||
|
||||
public bool IsBanned { get; set; }
|
||||
|
||||
public bool EditingEnabled { get; set; }
|
||||
|
||||
public bool CanJoin { get; set; }
|
||||
public WhiteboardMembershipStatus Status { get; set; }
|
||||
|
||||
public DateTime LastInteractedAt { get; set; }
|
||||
}
|
||||
@@ -24,9 +24,8 @@ public class WhiteboardMembershipRepository
|
||||
entity.Id.ToString(),
|
||||
entity.WhiteboardId.ToString(),
|
||||
entity.UserId.ToString(),
|
||||
entity.IsBanned,
|
||||
entity.EditingEnabled,
|
||||
entity.CanJoin,
|
||||
entity.Status,
|
||||
entity.LastInteractedAt
|
||||
);
|
||||
}
|
||||
@@ -38,18 +37,16 @@ public class WhiteboardMembershipRepository
|
||||
Id = new Guid(model.Id.IdValue),
|
||||
WhiteboardId = new Guid(model.WhiteboardId.IdValue),
|
||||
UserId = new Guid(model.UserId.IdValue),
|
||||
IsBanned = model.IsBanned.IsBannedValue,
|
||||
EditingEnabled = model.EditingEnabled.EditingEnabledValue,
|
||||
CanJoin = model.CanJoin.CanJoinValue,
|
||||
Status = model.Status,
|
||||
LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateEntity(WhiteboardMembership entity, Domain.Models.WhiteboardMembership.WhiteboardMembership model)
|
||||
{
|
||||
entity.IsBanned = model.IsBanned.IsBannedValue;
|
||||
entity.EditingEnabled = model.EditingEnabled.EditingEnabledValue;
|
||||
entity.CanJoin = model.CanJoin.CanJoinValue;
|
||||
entity.Status = model.Status;
|
||||
entity.LastInteractedAt = model.LastInteractedAt.LastInteractedAtValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
using AipsCore.Application.Abstract.MessageBroking;
|
||||
using AipsCore.Application.Common.Message.MoveShape;
|
||||
using AipsCore.Application.Abstract;
|
||||
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.Shapes;
|
||||
using AipsRT.Model.Whiteboard.Structs;
|
||||
@@ -15,33 +21,147 @@ public class WhiteboardHub : Hub
|
||||
{
|
||||
private readonly WhiteboardManager _whiteboardManager;
|
||||
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;
|
||||
_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)
|
||||
{
|
||||
if (!_whiteboardManager.WhiteboardExists(whiteboardId))
|
||||
await _whiteboardManager.AddWhiteboard(whiteboardId);
|
||||
{
|
||||
await _whiteboardManager.LoadWhiteboard(whiteboardId);
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, whiteboardId.ToString());
|
||||
|
||||
var state = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
||||
var whiteboard = _whiteboardManager.GetWhiteboard(whiteboardId)!;
|
||||
|
||||
_whiteboardManager.AddUserToWhiteboard(Guid.Parse(Context.UserIdentifier!), whiteboardId);
|
||||
var userId = CurrentUserId;
|
||||
var ownerId = whiteboard.OwnerId;
|
||||
|
||||
await Clients.Caller.SendAsync("InitWhiteboard", state);
|
||||
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId)
|
||||
.SendAsync("Joined", Context.UserIdentifier!);
|
||||
WhiteboardMembershipStatus status;
|
||||
|
||||
if (userId == ownerId)
|
||||
{
|
||||
status = WhiteboardMembershipStatus.Accepted;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = await _membershipService.GetMembershipStatus(whiteboardId, userId);
|
||||
}
|
||||
|
||||
_whiteboardManager.AddUserToWhiteboard(userId, whiteboardId);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.GroupExcept(whiteboard.WhiteboardId.ToString(),
|
||||
Context.ConnectionId).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)
|
||||
{
|
||||
var userId = CurrentUserId;
|
||||
_whiteboardManager.RemoveUserFromWhiteboard(userId);
|
||||
_whiteboardManager.GetWhiteboard(whiteboardId)?.RemoveActiveUser(userId);
|
||||
|
||||
await Clients.GroupExcept(whiteboardId.ToString(), Context.ConnectionId)
|
||||
.SendAsync("Leaved", Context.UserIdentifier!);
|
||||
|
||||
}
|
||||
|
||||
private Guid CurrentUserId => Guid.Parse(Context.UserIdentifier!);
|
||||
@@ -127,6 +247,6 @@ public class WhiteboardHub : Hub
|
||||
{
|
||||
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.Models.Whiteboard.Query.GetWhiteboardInfoRT;
|
||||
using AipsCore.Domain.Models.Shape.Enums;
|
||||
using AipsCore.Domain.Models.WhiteboardMembership.Enums;
|
||||
using AipsRT.Model.Whiteboard.Shapes.Map;
|
||||
using AipsRT.Model.Users;
|
||||
|
||||
namespace AipsRT.Model.Whiteboard;
|
||||
|
||||
@@ -26,8 +28,15 @@ public class GetWhiteboardService
|
||||
{
|
||||
WhiteboardId = entity.Id,
|
||||
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)
|
||||
{
|
||||
switch (shape.Type)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AipsRT.Model.Whiteboard.Shapes;
|
||||
using AipsRT.Model.Users;
|
||||
|
||||
namespace AipsRT.Model.Whiteboard;
|
||||
|
||||
@@ -7,6 +8,15 @@ public class Whiteboard
|
||||
public Guid WhiteboardId { 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; } = [];
|
||||
|
||||
@@ -38,4 +48,12 @@ public class Whiteboard
|
||||
Shapes.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;
|
||||
}
|
||||
|
||||
public async Task AddWhiteboard(Guid whiteboardId)
|
||||
public async Task LoadWhiteboard(Guid whiteboardId)
|
||||
{
|
||||
var getWhiteboardService = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<GetWhiteboardService>();
|
||||
var whiteboard = await getWhiteboardService.GetWhiteboard(whiteboardId);
|
||||
@@ -47,9 +47,9 @@ public class WhiteboardManager
|
||||
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)
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
using AipsCore.Application.Common.Message.ErrorMessage;
|
||||
using AipsCore.Infrastructure.DI;
|
||||
using AipsRT.Hubs;
|
||||
using AipsRT.Model.Memberships;
|
||||
using AipsRT.Model.Users;
|
||||
using AipsRT.Model.Whiteboard;
|
||||
using AipsRT.Services;
|
||||
using AipsRT.Services.Interfaces;
|
||||
using DotNetEnv;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
Env.Load("../../.env");
|
||||
if (File.Exists("../../.env"))
|
||||
{
|
||||
Env.Load("../../.env");
|
||||
}
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -15,6 +21,13 @@ builder.Configuration.AddEnvironmentVariables();
|
||||
builder.Services.AddSignalR();
|
||||
|
||||
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.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.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;
|
||||
|
||||
namespace AipsRT.Services.Interfaces;
|
||||
@@ -11,5 +14,9 @@ public interface IMessagingService
|
||||
Task CreateLine(Guid whiteboardId, Line line);
|
||||
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.Common.Message.AcceptUserRequestToJoin;
|
||||
using AipsCore.Application.Common.Message.AddArrow;
|
||||
using AipsCore.Application.Common.Message.AddLine;
|
||||
using AipsCore.Application.Common.Message.AddRectangle;
|
||||
using AipsCore.Application.Common.Message.AddTextShape;
|
||||
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.CreateLine;
|
||||
using AipsCore.Application.Models.Shape.Command.CreateRectangle;
|
||||
using AipsCore.Application.Models.Shape.Command.CreateTextShape;
|
||||
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.Services.Interfaces;
|
||||
|
||||
@@ -95,9 +101,27 @@ public class MessagingService : IMessagingService
|
||||
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);
|
||||
}
|
||||
|
||||
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.Models.Whiteboard.Command.CreateWhiteboard;
|
||||
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.GetWhiteboard;
|
||||
using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardHistory;
|
||||
using AipsCore.Application.Models.WhiteboardMembership.Command.CreateWhiteboardMembership;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Whiteboard = AipsCore.Infrastructure.Persistence.Whiteboard.Whiteboard;
|
||||
@@ -69,7 +69,7 @@ public class WhiteboardController : ControllerBase
|
||||
|
||||
[Authorize]
|
||||
[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);
|
||||
return Ok(result);
|
||||
|
||||
@@ -3,7 +3,10 @@ using AipsCore.Infrastructure.Persistence.Db;
|
||||
using AipsWebApi.Middleware;
|
||||
using DotNetEnv;
|
||||
|
||||
Env.Load("../../.env");
|
||||
if (File.Exists("../../.env"))
|
||||
{
|
||||
Env.Load("../../.env");
|
||||
}
|
||||
|
||||
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