Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15ac2de003 | ||
|
|
9334511f78 | ||
|
|
4b5800e3fb | ||
|
|
27c8075092 | ||
|
|
74b5c8e3a1 | ||
|
|
823a8c9d1b | ||
|
|
fc74b10766 | ||
|
|
ffdc36dbd2 | ||
|
|
81d8a76256 | ||
|
|
3a414dbf79 | ||
|
|
19b1c4295e | ||
|
|
cd9bb683c8 | ||
|
|
8a58fa6d34 | ||
|
|
af0f787f6a | ||
|
|
ce55972d64 | ||
|
|
6d3db24188 | ||
|
|
63b81a62e4 | ||
|
|
16bc48a491 | ||
|
|
be322e581b | ||
|
|
884c3a6f34 | ||
|
|
261c037f27 | ||
|
|
457285e194 | ||
|
|
cb274449a8 | ||
|
|
9628a4f60b | ||
|
|
dc690a49f0 | ||
|
|
efaeedce3f | ||
|
|
9231f5a9a6 | ||
|
|
d25db6a360 | ||
|
|
472185f752 | ||
|
|
92287837ec | ||
|
|
fe2d38cd12 | ||
|
|
be6b993d03 | ||
|
|
c5551d84fa | ||
|
|
c3c5e3a729 | ||
|
|
86777211a4 | ||
|
|
a3c98f8bd7 | ||
|
|
fcbd1a7294 | ||
|
|
92570ff64d | ||
|
|
4bf8d9d583 | ||
|
|
4a353a8baf | ||
|
|
3271a92164 | ||
|
|
d721e13108 | ||
|
|
46db1f45ac | ||
|
|
5ef48012fb | ||
|
|
1241a58f3a | ||
|
|
5a10794876 | ||
|
|
a003da0240 | ||
|
|
b21559bd0d | ||
|
|
f99dcce2e0 | ||
|
|
ed3afddf21 | ||
|
|
847c3ee696 | ||
|
|
7175f13b21 | ||
|
|
644bcf0294 | ||
|
|
d409c29c73 | ||
|
|
2b691e61bf | ||
|
|
d0595f758e | ||
|
|
4160881df9 | ||
|
|
a426a1a3a4 |
BIN
Producer.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
188
README.md
@@ -1,78 +1,124 @@
|
||||
Neste projeto experimental em Domain-Driven-Design com implementação de Aggregates + Event Sourcing + CQRS + Optimistic Concurrency a técnica de Event Sourcing é usada como ferramenta de auditoria das modificações do conteúdo do Blog. A fonte dos dados são os Domain Events registrados no Kafka, para permitir consultas de alta performance, foi criado um banco de dados MongoDB com o 'último estado já processado dos eventos'. Há um microsserviço auxiliar de autenticação. Tudo isso foi implementado em .NET Core/Standard compatível com Docker! Divirta-se!
|
||||
A solution based on a Event-Driven architecture with DDD and CQRS. The solution contains the following applications.
|
||||
* A Producer Web API which receives Commands to produce Domain Events. This one also receives Queries and returns JSON.
|
||||
* A Consumer Console App that reads the Event Stream and do a projection to a MongoDB database.
|
||||
* A Web API for authentication and JWT generation.
|
||||
|
||||
#### O Domínio
|
||||

|
||||
|
||||
#### As Aplicações desta Solução
|
||||
* **Producer**: Web API que recebe os comandos de edição de conteúdo, produz Eventos de Domínio e publica as mensagens em um tópico no Kafka.
|
||||
* **Consumer**: Aplicativo Console que consome as mensagens do Kafka, deserializa em Eventos de Domínio e aplica nas agregações persistindo no MongoDB o novo estado.
|
||||
* **Auth**: Web API que gera tokens de autenticação para acesso ao WebAPI.
|
||||
|
||||
#### Por onde começar?
|
||||
Há duas formas de iniciar a solução.
|
||||
|
||||
##### 1. O jeito fácil
|
||||
|
||||
Resolver os [pré-requisitos](https://github.com/ivanpaulovich/jambo/#prerequisitos), definir o projeto inicial como sendo o `docker-compose` e então apertar `Ctrl+F5` para executar todas as aplicações. Se tudo estiver correto, digite `docker ps` no seu terminal para verificar em quais portas cada aplicação está executando. Será algo assim:
|
||||
|
||||

|
||||
|
||||
A partir daí basta acessar:
|
||||
* Auth em http://localhost:32775/swagger/
|
||||
* Producer em http://localhost:32776/swagger/
|
||||
|
||||
Leia o [o jeito não tão fácil](https://github.com/ivanpaulovich/jambo/#2-o-jeito-não-tão-fácil) para entender como criar um Token no Auth API para consumir os serviços do Producer API via swagger.
|
||||
|
||||
##### 2. O jeito não tão fácil
|
||||
|
||||
A outra opção é inicializar aplicação por aplicação, seguindo o seguintes passos:
|
||||
|
||||
1. Execute o projeto **Jambo.Auth.WebAPI** e chame o método *Account/Token* com qualquer usuário e senha. *Guarde este token*.
|
||||

|
||||

|
||||
|
||||
3. Execute o projeto **Jambo.Producer.WebAPI** e clique no botão *Authorization* (topo direito da página).
|
||||
|
||||
Digite `bearer + valor_do_token` e clique em fechar. Algo assim:
|
||||

|
||||
Chame os métodos para manutenção dos dados do Blog, Posts e Comentários.
|
||||

|
||||
|
||||
2. Execute o projeto **Jambo.Consumer.Console** e garante que ele **contínua em execução**.
|
||||

|
||||
|
||||
4. Visualize suas modificações
|
||||

|
||||
|
||||
#### Demo
|
||||
*Em breve...*
|
||||
|
||||
#### Próximos passos?
|
||||
1. Publicar os containers no Azure.
|
||||
2. Criar um CI/CD para atualizar os containers a cada commit.
|
||||
3. Criar testes de unidade, testes automatizados.
|
||||
4. Consumir serviços externos.
|
||||
5. Implementação alternativa de barramento: Azure Event Hubs
|
||||
6. Implementação alternativa de snapshot: Azure Cosmos DB
|
||||
|
||||
#### Pré-requisitos
|
||||
[Checkout the Source Code on GitHub](https://github.com/ivanpaulovich/jambo)
|
||||
|
||||
#### Requirements
|
||||
* [Visual Studio 2017 + Update 3](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes)
|
||||
* [.NET SDK 2.0](https://www.microsoft.com/net/download/core)
|
||||
* [Docker](https://docs.docker.com/docker-for-windows/install/) (Opcional)
|
||||
* [Robomongo](https://robomongo.org/) (Opcional)
|
||||
* [.NET CORE SDK 2.0](https://www.microsoft.com/net/download/core)
|
||||
* [Docker](https://docs.docker.com/docker-for-windows/install/)
|
||||
|
||||
#### Agradecimentos
|
||||
Obrigado aos amigos que me estimularam a criar este projeto e estão sempre contribuindo e dando feedback.
|
||||
* [Vinicius Baldotto](https://github.com/Baldotto)
|
||||
* [André Paulovich](https://github.com/andrepaulovich)
|
||||
* André Mendes
|
||||
#### Environment setup
|
||||
|
||||
Obrigado de verdade!
|
||||
*If you already have valid connections for Kafka and MongoDB you could skip this step and go to Running the applications step.*
|
||||
|
||||
#### Deixe o seu feedback
|
||||
Agradeço todo comentário sobre o projeto. Envie suas dúvidas e sugestões no [Fórum](https://github.com/ivanpaulovich/jambo/issues).
|
||||
* Run the `./up-kafka-mongodb.sh` bash script to run Kafka and MongoDB as Docker Containers. Please wait until the ~800mb download to be complete.
|
||||
|
||||
#### Histórico de Versões
|
||||
* 12/set/2017:
|
||||
*Em breve...*
|
||||
```
|
||||
$ ./up-kafka-mongodb.sh
|
||||
Pulling mongodb (mongo:latest)...
|
||||
latest: Pulling from library/mongo
|
||||
Digest: sha256:2c55bcc870c269771aeade05fc3dd3657800540e0a48755876a1dc70db1e76d9
|
||||
Status: Downloaded newer image for mongo:latest
|
||||
Pulling kafka (spotify/kafka:latest)...
|
||||
latest: Pulling from spotify/kafka
|
||||
Digest: sha256:cf8f8f760b48a07fb99df24fab8201ec8b647634751e842b67103a25a388981b
|
||||
Status: Downloaded newer image for spotify/kafka:latest
|
||||
Creating setup_mongodb_1 ...
|
||||
Creating setup_kafka_1 ...
|
||||
Creating setup_mongodb_1
|
||||
Creating setup_mongodb_1 ... done
|
||||
```
|
||||
* Check if the data layer is ready with the following commands:
|
||||
|
||||
```
|
||||
$ docker images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
mongo latest d22888af0ce0 17 hours ago 361MB
|
||||
spotify/kafka latest a9e0a5b8b15e 11 months ago 443MB
|
||||
```
|
||||
|
||||
```
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
32452776153f spotify/kafka "supervisord -n" 2 days ago Up 2 days 0.0.0.0:2181->2181/tcp, 0.0.0.0:9092->9092/tcp setup_kafka_1
|
||||
ba28cf144478 mongo "docker-entrypoint..." 2 days ago Up 2 days 0.0.0.0:27017->27017/tcp setup_mongodb_1
|
||||
```
|
||||
|
||||
If Kafka is running well it will be working with the `10.0.75.1:9092` connection string.
|
||||
if MongoDB is running well it will be working at `mongodb://10.0.75.1:27017` connection string.
|
||||
|
||||
## Running the applications
|
||||
|
||||
You have two options to run the applications, one is by opening with Visual Studio 2017 and the other is by running dotnet core commands.
|
||||
|
||||
### Option 1 - Running with Visual Studio 2017
|
||||
|
||||
Open the three solutions on three Visual Studios them run the following projects.
|
||||
|
||||
* `Jambo.Auth.UI`.
|
||||
* `Jambo.Consumer.UI`.
|
||||
* `Jambo.Producer.UI`.
|
||||
|
||||
### Option 2 - Running with dotnet commands
|
||||
|
||||
#### How to run the Bearer Authencation API
|
||||
|
||||
1. Run the command `dotnet run` at `source\Auth\Jambo.Auth.UI` folder.
|
||||
```
|
||||
$ dotnet run
|
||||
Using launch settings from C:\git\jambo\source\Auth\Jambo.Auth.UI\Properties\launchSettings.json...
|
||||
Hosting environment: Development
|
||||
Content root path: C:\git\jambo\source\Auth\Jambo.Auth.UI
|
||||
Now listening on: http://localhost:16024
|
||||
Application started. Press Ctrl+C to shut down.
|
||||
```
|
||||
2. Navigate to the Swagger UI at (eg. http://localhost:16024/swagger).
|
||||
3. Post the following credentials:
|
||||
```
|
||||
{
|
||||
"username": "ivanpaulovich",
|
||||
"password": "mysecret"
|
||||
}
|
||||
```
|
||||
4. __Store the Bearer Token__ because you will need the token value to log on Producer API.
|
||||
```
|
||||
{
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhYzA4MmE3OS1lMWY3LTQ4MTktYmU1Mi1hOTQwMTBkM2VjZTciLCJzdWIiOiJzdHJpbmciLCJleHAiOjE1MTI0Nzg5ODgsImlzcyI6Imh0dHA6Ly9teWFjY291bnRhcGkiLCJhdWQiOiJodHRwOi8vbXlhY2NvdW50YXBpIn0.9YKGmKaptLBDcExHhPOQ3_j9TsdbkcRf8ZtvIkdq8Go",
|
||||
"expiration": "2017-12-05T13:03:08Z"
|
||||
}
|
||||
```
|
||||
#### How to run the Consumer API
|
||||
|
||||
1. Run the command `dotnet run` at `source\Consumer\Jambo.Consumer.UI` folder
|
||||
|
||||
```
|
||||
$ dotnet run
|
||||
11/5/2017 11:17:20 AM Waiting for events..
|
||||
11/5/2017 11:18:20 AM Waiting for events..
|
||||
11/5/2017 11:19:20 AM Waiting for events..
|
||||
11/5/2017 11:20:20 AM Waiting for events..
|
||||
11/5/2017 11:21:20 AM Waiting for events..
|
||||
11/5/2017 11:22:20 AM Waiting for events..
|
||||
```
|
||||
|
||||
3. __Attention:__ keep the Console App running for events processing.
|
||||
|
||||
#### How to run the Producer API
|
||||
|
||||

|
||||
|
||||
1. Run the command `dotnet run` at the `source\Producer\Jambo.Producer.UI` folder.
|
||||
|
||||
```
|
||||
$ dotnet run
|
||||
Using launch settings from C:\git\jambo\source\Producer\Jambo.Producer.UI\Properties\launchSettings.json...
|
||||
Hosting environment: Development
|
||||
Content root path: C:\git\jambo\source\Producer\Jambo.Producer.UI
|
||||
Now listening on: http://localhost:16959
|
||||
Application started. Press Ctrl+C to shut down.
|
||||
```
|
||||
|
||||
2. Navigate to the Swagger UI (eg. http://localhost:14398/swagger).
|
||||
|
||||
1
_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
||||
BIN
docs/Domain.pptx
BIN
images/Auth.PNG
|
Before Width: | Height: | Size: 52 KiB |
BIN
images/Auth1.PNG
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 39 KiB |
15
setup/docker-compose.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo
|
||||
ports:
|
||||
- 27017:27017
|
||||
kafka:
|
||||
image: spotify/kafka
|
||||
ports:
|
||||
- 2181:2181
|
||||
- 9092:9092
|
||||
environment:
|
||||
- ADVERTISED_HOST=10.0.75.1
|
||||
- ADVERTISED_PORT=9092
|
||||
1
setup/down-kafka-mongodb.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker-compose down
|
||||
1
setup/up-kafka-mongodb.sh
Normal file
@@ -0,0 +1 @@
|
||||
docker-compose up -d
|
||||
17
source/Auth/Jambo.Auth.Application/Commands/LoginCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Jambo.Auth.Application.Commands
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
[DataContract]
|
||||
public class LoginCommand
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,4 @@
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="CommandHandlers\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Auth.Application\Jambo.Auth.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -3,7 +3,7 @@
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:8546/",
|
||||
"applicationUrl": "http://localhost:15877/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
@@ -16,14 +16,14 @@
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Jambo.Auth.WebAPI": {
|
||||
"Jambo.Auth.Infrastructure": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "api/values",
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:8547/"
|
||||
"applicationUrl": "http://localhost:15878/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Jambo.Auth.Application.Auth
|
||||
namespace Jambo.Auth.UI
|
||||
{
|
||||
public class Config
|
||||
{
|
||||
@@ -1,18 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Jambo.Auth.Application.Auth;
|
||||
using Jambo.Auth.Application.Commands;
|
||||
|
||||
namespace Jambo.Auth.WebAPI.Controllers
|
||||
namespace Jambo.Auth.UI
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Jambo.Auth.Application.Commands;
|
||||
|
||||
[Route("api/[controller]")]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
@@ -27,7 +24,12 @@ namespace Jambo.Auth.WebAPI.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult Token([FromBody] LoginCommand loginCommand)
|
||||
{
|
||||
var token = GetJwtSecurityToken(loginCommand.Username);
|
||||
//
|
||||
// TODO: Implementar uma verificação
|
||||
// se loginCommand.Username e loginCommand.Password são válidos.
|
||||
//
|
||||
|
||||
var token = GetJwtSecurityToken(loginCommand);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
@@ -36,25 +38,25 @@ namespace Jambo.Auth.WebAPI.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
private JwtSecurityToken GetJwtSecurityToken(string user)
|
||||
private JwtSecurityToken GetJwtSecurityToken(LoginCommand user)
|
||||
{
|
||||
return new JwtSecurityToken(
|
||||
config.Issuer,
|
||||
config.Issuer,
|
||||
GetTokenClaims(user),
|
||||
expires: DateTime.UtcNow.AddMinutes(30),
|
||||
expires: DateTime.UtcNow.AddDays(7),
|
||||
signingCredentials: new SigningCredentials(
|
||||
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey)),
|
||||
SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
}
|
||||
|
||||
private static IEnumerable<Claim> GetTokenClaims(string user)
|
||||
private static IEnumerable<Claim> GetTokenClaims(LoginCommand user)
|
||||
{
|
||||
return new List<Claim>
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user)
|
||||
new Claim(JwtRegisteredClaimNames.Jti, user.Username),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.Password),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@ ARG source
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
COPY ${source:-obj/Docker/publish} .
|
||||
ENTRYPOINT ["dotnet", "Jambo.Auth.WebAPI.dll"]
|
||||
ENTRYPOINT ["dotnet", "Jambo.Auth.UI.dll"]
|
||||
@@ -2,11 +2,16 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701</NoWarn>
|
||||
<DocumentationFile>bin\Debug\netcoreapp2.0\Jambo.Auth.UI.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\" />
|
||||
<None Include="appsettings.Development.json" />
|
||||
<None Include="appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -14,10 +19,6 @@
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Auth.Application\Jambo.Auth.Application.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -1,15 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jambo.Auth.WebAPI
|
||||
namespace Jambo.Auth.UI
|
||||
{
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
27
source/Auth/Jambo.Auth.UI/Properties/launchSettings.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:16023/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Jambo.Auth.UI": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:16024/"
|
||||
}
|
||||
}
|
||||
}
|
||||
54
source/Auth/Jambo.Auth.UI/Startup.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace Jambo.Auth.UI
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.DescribeAllEnumsAsStrings();
|
||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
|
||||
{
|
||||
Title = "Auth API",
|
||||
Version = "v1",
|
||||
Description = "The Auth API",
|
||||
TermsOfService = "Terms Of Service"
|
||||
});
|
||||
});
|
||||
|
||||
services.AddScoped(
|
||||
s => new UI.Config(Configuration.GetSection("Security").GetValue<string>("SecretKey"),
|
||||
Configuration.GetSection("Security").GetValue<string>("Issuer")));
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseMvc();
|
||||
|
||||
app.UseSwagger()
|
||||
.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
37
source/Auth/Jambo.Auth.sln
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Auth.Application", "Jambo.Auth.Application\Jambo.Auth.Application.csproj", "{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Auth.Infrastructure", "Jambo.Auth.Infrastructure\Jambo.Auth.Infrastructure.csproj", "{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Auth.UI", "Jambo.Auth.UI\Jambo.Auth.UI.csproj", "{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {54AE4BA0-6081-4892-A9B9-3F8D15D994C8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -12,7 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,76 @@
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess
|
||||
{
|
||||
using Jambo.Domain.Model;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Driver;
|
||||
|
||||
public class MongoContext
|
||||
{
|
||||
private readonly MongoClient mongoClient;
|
||||
private readonly IMongoDatabase database;
|
||||
|
||||
public MongoContext(string connectionString, string databaseName)
|
||||
{
|
||||
this.mongoClient = new MongoClient(connectionString);
|
||||
this.database = mongoClient.GetDatabase(databaseName);
|
||||
Map();
|
||||
}
|
||||
|
||||
public void DatabaseReset(string databaseName)
|
||||
{
|
||||
mongoClient.DropDatabase(databaseName);
|
||||
}
|
||||
|
||||
public IMongoCollection<Blog> Blogs
|
||||
{
|
||||
get
|
||||
{
|
||||
return database.GetCollection<Blog>("Blogs");
|
||||
}
|
||||
}
|
||||
|
||||
public IMongoCollection<Post> Posts
|
||||
{
|
||||
get
|
||||
{
|
||||
return database.GetCollection<Post>("Posts");
|
||||
}
|
||||
}
|
||||
|
||||
private void Map()
|
||||
{
|
||||
BsonClassMap.RegisterClassMap<Entity>(cm =>
|
||||
{
|
||||
cm.MapIdProperty(c => c.Id);
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<AggregateRoot>(cm =>
|
||||
{
|
||||
cm.MapProperty(c => c.Version).SetElementName("_version");
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<Blog>(cm =>
|
||||
{
|
||||
cm.MapField("url").SetElementName("url");
|
||||
cm.MapField("rating").SetElementName("rating");
|
||||
cm.MapField("enabled").SetElementName("enabled");
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<Post>(cm =>
|
||||
{
|
||||
cm.MapField("title").SetElementName("title");
|
||||
cm.MapField("content").SetElementName("content");
|
||||
cm.MapField("blogId").SetElementName("blogId");
|
||||
cm.MapField("enabled").SetElementName("enabled");
|
||||
cm.MapField("published").SetElementName("published");
|
||||
});
|
||||
|
||||
BsonClassMap.RegisterClassMap<Comment>(cm =>
|
||||
{
|
||||
cm.MapField("message").SetElementName("message");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Blogs
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs
|
||||
{
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class BlogReadOnlyRepository : IBlogReadOnlyRepository
|
||||
{
|
||||
private readonly MongoContext _mongoContext;
|
||||
@@ -1,10 +1,10 @@
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Blogs
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs
|
||||
{
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class BlogWriteOnlyRepository : IBlogWriteOnlyRepository
|
||||
{
|
||||
private readonly MongoContext mongoContext;
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts
|
||||
{
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class PostReadOnlyRepository : IPostReadOnlyRepository
|
||||
{
|
||||
private readonly MongoContext _mongoContext;
|
||||
|
||||
public PostReadOnlyRepository(MongoContext mongoContext)
|
||||
{
|
||||
_mongoContext = mongoContext;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Post>> GetBlogPosts(Guid blogId)
|
||||
{
|
||||
return await _mongoContext.Posts.Find(e => e.BlogId == blogId).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Post> GetPost(Guid id)
|
||||
{
|
||||
return await _mongoContext.Posts.Find(e => e.Id == id).SingleAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Infrastructure.Repositories.Posts
|
||||
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts
|
||||
{
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using MongoDB.Driver;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class PostWriteOnlyRepository : IPostWriteOnlyRepository
|
||||
{
|
||||
private readonly MongoContext _mongoContext;
|
||||
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="Autofac.Configuration" Version="4.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="Confluent.Kafka" Version="0.11.0" NoWarn="NU1701" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,28 +1,21 @@
|
||||
using Autofac;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.Infrastructure;
|
||||
using Jambo.Infrastructure.Repositories;
|
||||
using Jambo.Infrastructure.Repositories.Blogs;
|
||||
using Jambo.Infrastructure.Repositories.Posts;
|
||||
|
||||
namespace Jambo.Consumer.IoC
|
||||
namespace Jambo.Consumer.Infrastructure.Modules
|
||||
{
|
||||
using Autofac;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs;
|
||||
using Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts;
|
||||
using Jambo.Consumer.Infrastructure.DataAccess;
|
||||
|
||||
public class ApplicationModule : Module
|
||||
{
|
||||
public readonly string connectionString;
|
||||
public readonly string database;
|
||||
|
||||
public ApplicationModule(string connectionString, string database)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.database = database;
|
||||
}
|
||||
public string ConnectionString { get; set; }
|
||||
public string DatabaseName { get; set; }
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
MongoContext mongoContext = new MongoContext(connectionString, database);
|
||||
mongoContext.DatabaseReset(database);
|
||||
MongoContext mongoContext = new MongoContext(ConnectionString, DatabaseName);
|
||||
mongoContext.DatabaseReset(DatabaseName);
|
||||
|
||||
builder.Register(c => mongoContext)
|
||||
.As<MongoContext>().SingleInstance();
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Jambo.Consumer.Infrastructure.Modules
|
||||
{
|
||||
using Autofac;
|
||||
using Jambo.Consumer.Infrastructure.ServiceBus;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
|
||||
public class BusModule : Module
|
||||
{
|
||||
public string BrokerList { get; set; }
|
||||
public string Topic { get; set; }
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterType<Bus>()
|
||||
.As<ISubscriber>()
|
||||
.WithParameter("brokerList", BrokerList)
|
||||
.WithParameter("topic", Topic)
|
||||
.SingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Jambo.Consumer.Infrastructure.Modules
|
||||
{
|
||||
using Autofac;
|
||||
using Autofac.Features.Variance;
|
||||
using Jambo.Consumer.Application.DomainEventHandlers.Blogs;
|
||||
using MediatR;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
public class MediatRModule : Autofac.Module
|
||||
{
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterSource(new ContravariantRegistrationSource());
|
||||
|
||||
builder
|
||||
.RegisterType<Mediator>()
|
||||
.As<IMediator>()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder
|
||||
.Register<SingleInstanceFactory>(ctx => {
|
||||
var c = ctx.Resolve<IComponentContext>();
|
||||
return t => { object o; return c.TryResolve(t, out o) ? o : null; };
|
||||
})
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder
|
||||
.Register<MultiInstanceFactory>(ctx => {
|
||||
var c = ctx.Resolve<IComponentContext>();
|
||||
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
|
||||
})
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterAssemblyTypes(typeof(BlogCreatedEventHandler).GetTypeInfo().Assembly).AsImplementedInterfaces(); // via assembly scan
|
||||
}
|
||||
}
|
||||
}
|
||||
121
source/Consumer/Jambo.Consumer.Infrastructure/ServiceBus/Bus.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
namespace Jambo.Consumer.Infrastructure.ServiceBus
|
||||
{
|
||||
using Confluent.Kafka;
|
||||
using Confluent.Kafka.Serialization;
|
||||
using Jambo.Domain.Exceptions;
|
||||
using Jambo.Domain.Model;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
public class Bus : ISubscriber
|
||||
{
|
||||
public readonly string brokerList;
|
||||
public readonly string topic;
|
||||
|
||||
private static Dictionary<string, object> constructConfig(string brokerList, bool enableAutoCommit) =>
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
{ "group.id", "jambo-consumer" },
|
||||
{ "enable.auto.commit", enableAutoCommit },
|
||||
{ "auto.commit.interval.ms", 5000 },
|
||||
{ "statistics.interval.ms", 60000 },
|
||||
{ "bootstrap.servers", brokerList },
|
||||
{ "default.topic.config", new Dictionary<string, object>()
|
||||
{
|
||||
{ "auto.offset.reset", "smallest" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public Bus(string brokerList, string topic)
|
||||
{
|
||||
this.brokerList = brokerList;
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public void Listen(IMediator mediator)
|
||||
{
|
||||
using (var consumer = new Consumer<string, string>(constructConfig(brokerList, true), new StringDeserializer(Encoding.UTF8), new StringDeserializer(Encoding.UTF8)))
|
||||
{
|
||||
consumer.OnPartitionEOF += (_, end)
|
||||
=> Console.WriteLine($"Reached end of topic {end.Topic} partition {end.Partition}, next message will be at offset {end.Offset}");
|
||||
|
||||
consumer.OnError += (_, error)
|
||||
=> Console.WriteLine($"Error: {error}");
|
||||
|
||||
consumer.OnConsumeError += (_, msg)
|
||||
=> Console.WriteLine($"Error consuming from topic/partition/offset {msg.Topic}/{msg.Partition}/{msg.Offset}: {msg.Error}");
|
||||
|
||||
consumer.OnOffsetsCommitted += (_, commit) =>
|
||||
{
|
||||
Console.WriteLine($"[{string.Join(", ", commit.Offsets)}]");
|
||||
|
||||
if (commit.Error)
|
||||
{
|
||||
Console.WriteLine($"Failed to commit offsets: {commit.Error}");
|
||||
}
|
||||
Console.WriteLine($"Successfully committed offsets: [{string.Join(", ", commit.Offsets)}]");
|
||||
};
|
||||
|
||||
consumer.OnPartitionsAssigned += (_, partitions) =>
|
||||
{
|
||||
Console.WriteLine($"Assigned partitions: [{string.Join(", ", partitions)}], member id: {consumer.MemberId}");
|
||||
consumer.Assign(partitions);
|
||||
};
|
||||
|
||||
consumer.OnPartitionsRevoked += (_, partitions) =>
|
||||
{
|
||||
Console.WriteLine($"Revoked partitions: [{string.Join(", ", partitions)}]");
|
||||
consumer.Unassign();
|
||||
};
|
||||
|
||||
consumer.OnStatistics += (_, json)
|
||||
=> Console.WriteLine($"Statistics: {json}");
|
||||
|
||||
consumer.Subscribe(this.topic);
|
||||
|
||||
Console.WriteLine($"Subscribed to: [{string.Join(", ", consumer.Subscription)}]");
|
||||
|
||||
var cancelled = false;
|
||||
Console.CancelKeyPress += (_, e) =>
|
||||
{
|
||||
e.Cancel = true; // prevent the process from terminating.
|
||||
cancelled = true;
|
||||
};
|
||||
|
||||
Console.WriteLine("Ctrl-C to exit.");
|
||||
while (!cancelled)
|
||||
{
|
||||
Message<string, string> msg;
|
||||
if (consumer.Consume(out msg, TimeSpan.FromSeconds(1)))
|
||||
{
|
||||
Console.WriteLine($"Topic: {msg.Topic} Partition: {msg.Partition} Offset: {msg.Offset} {msg.Value}");
|
||||
|
||||
try
|
||||
{
|
||||
Type eventType = Type.GetType(msg.Key);
|
||||
DomainEvent domainEvent = (DomainEvent)JsonConvert.DeserializeObject(msg.Value, eventType);
|
||||
mediator.Send(domainEvent).Wait();
|
||||
}
|
||||
catch (DomainException ex)
|
||||
{
|
||||
Console.WriteLine(ex.BusinessMessage);
|
||||
}
|
||||
catch (TransactionConflictException ex)
|
||||
{
|
||||
Console.WriteLine(ex.DomainEvent.ToString());
|
||||
}
|
||||
catch (JamboException ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@ ARG source
|
||||
WORKDIR /app
|
||||
EXPOSE 80
|
||||
COPY ${source:-obj/Docker/publish} .
|
||||
ENTRYPOINT ["dotnet", "Jambo.Consumer.Console.dll"]
|
||||
ENTRYPOINT ["dotnet", "Jambo.Consumer.UI.dll"]
|
||||
@@ -10,35 +10,26 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="Autofac.Configuration" Version="4.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="Confluent.Kafka" Version="0.11.0" NoWarn="NU1701" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Consumer.IoC\Jambo.Consumer.IoC.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Consumer.Infrastructure\Jambo.Consumer.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update=".dockerignore">
|
||||
<DependentUpon>Dockerfile</DependentUpon>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -1,20 +1,20 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO;
|
||||
|
||||
namespace Jambo.Consumer.Console
|
||||
namespace Jambo.Consumer.Infrastructure
|
||||
{
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.IO;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile("autofac.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
IConfigurationRoot configuration = builder.Build();
|
||||
|
||||
IServiceCollection serviceCollection = new ServiceCollection();
|
||||
|
||||
Startup startup = new Startup(configuration);
|
||||
43
source/Consumer/Jambo.Consumer.UI/Startup.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace Jambo.Consumer.Infrastructure
|
||||
{
|
||||
using Autofac;
|
||||
using Autofac.Configuration;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
public class Startup
|
||||
{
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
IServiceProvider serviceProvider;
|
||||
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
ContainerBuilder builder = new ContainerBuilder();
|
||||
builder.Populate(services);
|
||||
builder.RegisterModule(new ConfigurationModule(Configuration));
|
||||
|
||||
serviceProvider = new AutofacServiceProvider(builder.Build());
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
IMediator mediator = serviceProvider.GetService<IMediator>();
|
||||
ISubscriber subscriber = serviceProvider.GetService<ISubscriber>();
|
||||
|
||||
subscriber.Listen(mediator);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
source/Consumer/Jambo.Consumer.UI/appsettings.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
24
source/Consumer/Jambo.Consumer.UI/autofac.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"defaultAssembly": "Jambo.Consumer.Infrastructure",
|
||||
"modules": [
|
||||
{
|
||||
"type": "Jambo.Consumer.Infrastructure.Modules.ApplicationModule",
|
||||
"properties": {
|
||||
"ConnectionString": "mongodb://10.0.75.1:27017",
|
||||
"DatabaseName": "jambov32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Jambo.Consumer.Infrastructure.Modules.BusModule",
|
||||
"properties": {
|
||||
"BrokerList": "10.0.75.1:9092",
|
||||
"Topic": "jambov32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Jambo.Consumer.Infrastructure.Modules.MediatRModule",
|
||||
"properties": {
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
49
source/Consumer/Jambo.Consumer.sln
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.Application", "Jambo.Consumer.Application\Jambo.Consumer.Application.csproj", "{601F90B1-4BE4-462E-8595-AAAA49B80405}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.Infrastructure", "Jambo.Consumer.Infrastructure\Jambo.Consumer.Infrastructure.csproj", "{645C9138-DA59-48C8-A15E-D720874DD148}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.UI", "Jambo.Consumer.UI\Jambo.Consumer.UI.csproj", "{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Domain", "..\Shared\Jambo.Domain\Jambo.Domain.csproj", "{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Domain.UnitTests", "..\Shared\Jambo.Domain.UnitTests\Jambo.Domain.UnitTests.csproj", "{04732413-3F55-4194-B406-2F84666B1F6C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{645C9138-DA59-48C8-A15E-D720874DD148}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{04732413-3F55-4194-B406-2F84666B1F6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{04732413-3F55-4194-B406-2F84666B1F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04732413-3F55-4194-B406-2F84666B1F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04732413-3F55-4194-B406-2F84666B1F6C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {54AE4BA0-6081-4892-A9B9-3F8D15D994C8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,14 +0,0 @@
|
||||
using Jambo.Domain.ServiceBus;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Jambo.Application.DomainEventHandlers
|
||||
{
|
||||
public interface IEvent<T> : IRequest<T>
|
||||
where T : DomainEvent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Jambo.Auth.Application.Commands
|
||||
{
|
||||
[DataContract]
|
||||
public class LoginCommand : IRequest
|
||||
{
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Username { get; private set; }
|
||||
|
||||
[Required]
|
||||
[DataMember]
|
||||
public string Password { get; private set; }
|
||||
|
||||
public LoginCommand()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Jambo.Auth.IoC
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using Jambo.Auth.Application.Auth;
|
||||
|
||||
namespace Jambo.Auth.WebAPI
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMvc();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.DescribeAllEnumsAsStrings();
|
||||
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
|
||||
{
|
||||
Title = "Auth API",
|
||||
Version = "v1",
|
||||
Description = "The Auth API",
|
||||
TermsOfService = "Terms Of Service"
|
||||
});
|
||||
});
|
||||
|
||||
services.AddScoped(
|
||||
s => new Config(Configuration.GetSection("Security").GetValue<string>("SecretKey"),
|
||||
Configuration.GetSection("Security").GetValue<string>("Issuer")));
|
||||
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.SaveToken = true;
|
||||
options.RequireHttpsMetadata = false;
|
||||
options.TokenValidationParameters = new TokenValidationParameters()
|
||||
{
|
||||
ValidIssuer = Configuration.GetSection("Security").GetValue<string>("Issuer"),
|
||||
ValidAudience = Configuration.GetSection("Security").GetValue<string>("Issuer"),
|
||||
IssuerSigningKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(
|
||||
Configuration.GetSection("Security").GetValue<string>("SecretKey")))
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseMvc();
|
||||
|
||||
app.UseSwagger()
|
||||
.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Jambo.ServiceBus;
|
||||
using Newtonsoft.Json;
|
||||
using MediatR;
|
||||
using System.Reflection;
|
||||
using Autofac;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Jambo.Consumer.IoC;
|
||||
using System.Threading;
|
||||
using Jambo.Consumer.Application.DomainEventHandlers.Blogs;
|
||||
using Jambo.Domain.Model;
|
||||
|
||||
namespace Jambo.Consumer.Console
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
IServiceProvider serviceProvider;
|
||||
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddMediatR(typeof(BlogCreatedEventHandler).GetTypeInfo().Assembly);
|
||||
|
||||
ContainerBuilder container = new ContainerBuilder();
|
||||
container.Populate(services);
|
||||
|
||||
container.RegisterModule(new ApplicationModule(
|
||||
Configuration.GetSection("MongoDB").GetValue<string>("ConnectionString"),
|
||||
Configuration.GetSection("MongoDB").GetValue<string>("Database")));
|
||||
|
||||
container.RegisterModule(new BusModule(
|
||||
Configuration.GetSection("ServiceBus").GetValue<string>("ConnectionString"),
|
||||
Configuration.GetSection("ServiceBus").GetValue<string>("Topic")));
|
||||
|
||||
serviceProvider = new AutofacServiceProvider(container.Build());
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
IMediator mediator = serviceProvider.GetService<IMediator>();
|
||||
ISubscriber subscriber = serviceProvider.GetService<ISubscriber>();
|
||||
|
||||
subscriber.Listen(mediator);
|
||||
|
||||
while (true)
|
||||
{
|
||||
System.Console.WriteLine(DateTime.Now.ToString());
|
||||
Thread.Sleep(1000 * 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"MongoDB": {
|
||||
"ConnectionString": "mongodb://10.0.75.1:27017",
|
||||
"Database": "jambov32"
|
||||
},
|
||||
|
||||
"ServiceBus": {
|
||||
"ConnectionString": "10.0.75.1:9092",
|
||||
"Topic": "jambov32"
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Autofac;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.Infrastructure;
|
||||
using Jambo.ServiceBus.Kafka;
|
||||
using MediatR;
|
||||
|
||||
namespace Jambo.Consumer.IoC
|
||||
{
|
||||
public class BusModule : Module
|
||||
{
|
||||
private readonly string connectionString;
|
||||
private readonly string topic;
|
||||
|
||||
public BusModule(string connectionString, string topic)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.Register(c => new Config(connectionString, topic)).As<Config>().SingleInstance();
|
||||
builder.RegisterType<Bus>().As<ISubscriber>().SingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Infrastructure\Jambo.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus.Kafka\Jambo.ServiceBus.Kafka.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace Jambo.Domain.UnitTests
|
||||
{
|
||||
public class UnitTest1
|
||||
{
|
||||
[Fact]
|
||||
public void Test1()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using Jambo.Producer.Application.Commands;
|
||||
using Jambo.Producer.Application.Commands.Blogs;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Jambo.ServiceBus;
|
||||
|
||||
namespace Jambo.Producer.Application.CommandHandlers.Blogs
|
||||
{
|
||||
public class CreatePostCommandHandler : IAsyncRequestHandler<CreateBlogCommand, Guid>
|
||||
{
|
||||
private readonly IPublisher bus;
|
||||
|
||||
public CreatePostCommandHandler(IPublisher bus)
|
||||
{
|
||||
this.bus = bus ?? throw new ArgumentNullException(nameof(bus));
|
||||
}
|
||||
|
||||
public async Task<Guid> Handle(CreateBlogCommand command)
|
||||
{
|
||||
Blog blog = Blog.Create();
|
||||
blog.Start();
|
||||
blog.UpdateUrl(Url.Create(command.Url));
|
||||
|
||||
await bus.Publish(blog.GetEvents(), command.Header);
|
||||
|
||||
return blog.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
|
||||
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,43 +0,0 @@
|
||||
using Autofac;
|
||||
using Jambo.Producer.Application.Queries;
|
||||
using Jambo.Domain.Model.Blogs;
|
||||
using Jambo.Domain.Model.Posts;
|
||||
using Jambo.Infrastructure;
|
||||
using Jambo.Infrastructure.Repositories;
|
||||
using Jambo.Infrastructure.Repositories.Blogs;
|
||||
using Jambo.Infrastructure.Repositories.Posts;
|
||||
|
||||
namespace Jambo.Producer.IoC
|
||||
{
|
||||
public class ApplicationModule : Module
|
||||
{
|
||||
public readonly string connectionString;
|
||||
public readonly string database;
|
||||
|
||||
public ApplicationModule(string connectionString, string database)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.Register(c => new BlogQueries(connectionString, database))
|
||||
.As<IBlogQueries>();
|
||||
|
||||
builder.Register(c => new PostQueries(connectionString, database))
|
||||
.As<IPostQueries>();
|
||||
|
||||
builder.Register(c => new MongoContext(connectionString, database))
|
||||
.As<MongoContext>().SingleInstance();
|
||||
|
||||
builder.RegisterType<BlogReadOnlyRepository>()
|
||||
.As<IBlogReadOnlyRepository>()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterType<PostReadOnlyRepository>()
|
||||
.As<IPostReadOnlyRepository>()
|
||||
.InstancePerLifetimeScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Autofac;
|
||||
using Jambo.ServiceBus;
|
||||
using Jambo.ServiceBus.Kafka;
|
||||
|
||||
namespace Jambo.Producer.IoC
|
||||
{
|
||||
public class BusModule : Module
|
||||
{
|
||||
private readonly string connectionString;
|
||||
private readonly string topic;
|
||||
|
||||
public BusModule(string connectionString, string topic)
|
||||
{
|
||||
this.connectionString = connectionString;
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.Register(c => new Config(connectionString, topic)).As<Config>().SingleInstance();
|
||||
builder.RegisterType<Bus>().As<IPublisher>().SingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;1705;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="4.6.0" />
|
||||
<PackageReference Include="MediatR" Version="3.0.1" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Infrastructure\Jambo.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Jambo.Producer.Application\Jambo.Producer.Application.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus.Kafka\Jambo.ServiceBus.Kafka.csproj" />
|
||||
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,27 +0,0 @@
|
||||
using Jambo.Domain.Exceptions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jambo.Producer.WebAPI.Filters
|
||||
{
|
||||
public class DomainExceptionFilter : IExceptionFilter
|
||||
{
|
||||
public void OnException(ExceptionContext context)
|
||||
{
|
||||
BlogDomainException blogDomainException = context.Exception as BlogDomainException;
|
||||
if (blogDomainException != null)
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(blogDomainException.BusinessMessage);
|
||||
|
||||
context.Result = new BadRequestObjectResult(json);
|
||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
@page
|
||||
@inject Jambo.Producer.Application.Queries.IBlogQueries blogQueries
|
||||
@using Microsoft.AspNetCore.Mvc.RazorPages
|
||||
@using System.Dynamic
|
||||
|
||||
<div>
|
||||
<h1><a href="/">Blogs</a></h1>
|
||||
|
||||
@foreach (dynamic blog in blogQueries.GetBlogsAsync().Result)
|
||||
{
|
||||
<h2><a href="/Posts?blogId=@blog._id">@blog.url (@blog.rating)</a></h2>
|
||||
}
|
||||
</div>
|
||||
@@ -1,43 +0,0 @@
|
||||
@page
|
||||
@model IndexModel
|
||||
@using Microsoft.AspNetCore.Mvc.RazorPages
|
||||
@using System.Dynamic
|
||||
|
||||
@functions {
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public readonly Jambo.Producer.Application.Queries.IBlogQueries _blogQueries;
|
||||
public readonly Jambo.Producer.Application.Queries.IPostQueries _postQueries;
|
||||
|
||||
public IndexModel(Jambo.Producer.Application.Queries.IBlogQueries blogQueries,
|
||||
Jambo.Producer.Application.Queries.IPostQueries postQueries)
|
||||
{
|
||||
_blogQueries = blogQueries;
|
||||
_postQueries = postQueries;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Blog = _blogQueries.GetBlogAsync(Guid.Parse(Request.Query["blogId"])).Result;
|
||||
Posts = _postQueries.GetPostsAsync(Guid.Parse(Request.Query["blogId"])).Result;
|
||||
}
|
||||
|
||||
public dynamic Blog { get; private set; }
|
||||
public IEnumerable<dynamic> Posts { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
<div>
|
||||
<h1><a href="/">Blogs</a></h1>
|
||||
@*<h2><a href="/Posts?blogId=@Model.Blog._Id">@Model.Blog.Url</a></h2>*@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@foreach (dynamic post in Model.Posts)
|
||||
{
|
||||
<div>
|
||||
<h3><a href="/Posts/Single?id=@post._id">@post.title</a></h3>
|
||||
<p>@post.Content</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -1,40 +0,0 @@
|
||||
@page
|
||||
@model IndexModel
|
||||
@using Microsoft.AspNetCore.Mvc.RazorPages
|
||||
@using System.Dynamic
|
||||
|
||||
@functions {
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
public readonly Jambo.Producer.Application.Queries.IBlogQueries _blogQueries;
|
||||
public readonly Jambo.Producer.Application.Queries.IPostQueries _postQueries;
|
||||
|
||||
public IndexModel(Jambo.Producer.Application.Queries.IBlogQueries blogQueries,
|
||||
Jambo.Producer.Application.Queries.IPostQueries postQueries)
|
||||
{
|
||||
_blogQueries = blogQueries;
|
||||
_postQueries = postQueries;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Post = _postQueries.GetPostAsync(Guid.Parse(Request.Query["Id"])).Result;
|
||||
Blog = _blogQueries.GetBlogAsync(Post.BlogId).Result;
|
||||
}
|
||||
|
||||
public dynamic Blog { get; private set; }
|
||||
public dynamic Post { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
<div>
|
||||
<h1><a href="/">Blogs</a></h1>
|
||||
<h2><a href="/Posts?blogId=@Model.Blog.Id">@Model.Blog.Url</a></h2>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<h3><a href="/Posts?id=@Model.Post.Id">@Model.Post.Title</a></h3>
|
||||
<p>@Model.Post.Content</p>
|
||||
</div>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jambo.Producer.WebAPI
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
BuildWebHost(args).Run();
|
||||
}
|
||||
|
||||
public static IWebHost BuildWebHost(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
ASP.NET MVC core dependencies have been added to the project.
|
||||
(These dependencies include packages required to enable scaffolding)
|
||||
|
||||
However you may still need to do make changes to your project.
|
||||
|
||||
1. Suggested changes to Startup class:
|
||||
1.1 Add a constructor:
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
1.2 Add MVC services:
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Add framework services.
|
||||
services.AddMvc();
|
||||
}
|
||||
|
||||
1.3 Configure web app to use use Configuration and use MVC routing:
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "asp.net",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap": "3.3.6",
|
||||
"jquery": "2.2.0",
|
||||
"jquery-validation": "1.14.0",
|
||||
"jquery-validation-unobtrusive": "3.2.6"
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "bootstrap",
|
||||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
|
||||
"keywords": [
|
||||
"css",
|
||||
"js",
|
||||
"less",
|
||||
"mobile-first",
|
||||
"responsive",
|
||||
"front-end",
|
||||
"framework",
|
||||
"web"
|
||||
],
|
||||
"homepage": "http://getbootstrap.com",
|
||||
"license": "MIT",
|
||||
"moduleType": "globals",
|
||||
"main": [
|
||||
"less/bootstrap.less",
|
||||
"dist/js/bootstrap.js"
|
||||
],
|
||||
"ignore": [
|
||||
"/.*",
|
||||
"_config.yml",
|
||||
"CNAME",
|
||||
"composer.json",
|
||||
"CONTRIBUTING.md",
|
||||
"docs",
|
||||
"js/tests",
|
||||
"test-infra"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "1.9.1 - 2"
|
||||
},
|
||||
"version": "3.3.6",
|
||||
"_release": "3.3.6",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v3.3.6",
|
||||
"commit": "81df608a40bf0629a1dc08e584849bb1e43e0b7a"
|
||||
},
|
||||
"_source": "https://github.com/twbs/bootstrap.git",
|
||||
"_target": "3.3.6",
|
||||
"_originalSource": "bootstrap"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
Bootstrap uses [GitHub's Releases feature](https://github.com/blog/1547-release-your-software) for its changelogs.
|
||||
|
||||
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap.
|
||||
|
||||
Release announcement posts on [the official Bootstrap blog](http://blog.getbootstrap.com) contain summaries of the most noteworthy changes made in each release.
|
||||
@@ -1,533 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap's Gruntfile
|
||||
* http://getbootstrap.com
|
||||
* Copyright 2013-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
module.exports = function (grunt) {
|
||||
'use strict';
|
||||
|
||||
// Force use of Unix newlines
|
||||
grunt.util.linefeed = '\n';
|
||||
|
||||
RegExp.quote = function (string) {
|
||||
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
};
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var npmShrinkwrap = require('npm-shrinkwrap');
|
||||
var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');
|
||||
var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
|
||||
var getLessVarsData = function () {
|
||||
var filePath = path.join(__dirname, 'less/variables.less');
|
||||
var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
|
||||
var parser = new BsLessdocParser(fileContent);
|
||||
return { sections: parser.parseFile() };
|
||||
};
|
||||
var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
|
||||
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
|
||||
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
|
||||
|
||||
Object.keys(configBridge.paths).forEach(function (key) {
|
||||
configBridge.paths[key].forEach(function (val, i, arr) {
|
||||
arr[i] = path.join('./docs/assets', val);
|
||||
});
|
||||
});
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
|
||||
// Metadata.
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
banner: '/*!\n' +
|
||||
' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
|
||||
' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
|
||||
' * Licensed under the <%= pkg.license %> license\n' +
|
||||
' */\n',
|
||||
jqueryCheck: configBridge.config.jqueryCheck.join('\n'),
|
||||
jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),
|
||||
|
||||
// Task configuration.
|
||||
clean: {
|
||||
dist: 'dist',
|
||||
docs: 'docs/dist'
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: 'js/.jshintrc'
|
||||
},
|
||||
grunt: {
|
||||
options: {
|
||||
jshintrc: 'grunt/.jshintrc'
|
||||
},
|
||||
src: ['Gruntfile.js', 'package.js', 'grunt/*.js']
|
||||
},
|
||||
core: {
|
||||
src: 'js/*.js'
|
||||
},
|
||||
test: {
|
||||
options: {
|
||||
jshintrc: 'js/tests/unit/.jshintrc'
|
||||
},
|
||||
src: 'js/tests/unit/*.js'
|
||||
},
|
||||
assets: {
|
||||
src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
|
||||
}
|
||||
},
|
||||
|
||||
jscs: {
|
||||
options: {
|
||||
config: 'js/.jscsrc'
|
||||
},
|
||||
grunt: {
|
||||
src: '<%= jshint.grunt.src %>'
|
||||
},
|
||||
core: {
|
||||
src: '<%= jshint.core.src %>'
|
||||
},
|
||||
test: {
|
||||
src: '<%= jshint.test.src %>'
|
||||
},
|
||||
assets: {
|
||||
options: {
|
||||
requireCamelCaseOrUpperCaseIdentifiers: null
|
||||
},
|
||||
src: '<%= jshint.assets.src %>'
|
||||
}
|
||||
},
|
||||
|
||||
concat: {
|
||||
options: {
|
||||
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',
|
||||
stripBanners: false
|
||||
},
|
||||
bootstrap: {
|
||||
src: [
|
||||
'js/transition.js',
|
||||
'js/alert.js',
|
||||
'js/button.js',
|
||||
'js/carousel.js',
|
||||
'js/collapse.js',
|
||||
'js/dropdown.js',
|
||||
'js/modal.js',
|
||||
'js/tooltip.js',
|
||||
'js/popover.js',
|
||||
'js/scrollspy.js',
|
||||
'js/tab.js',
|
||||
'js/affix.js'
|
||||
],
|
||||
dest: 'dist/js/<%= pkg.name %>.js'
|
||||
}
|
||||
},
|
||||
|
||||
uglify: {
|
||||
options: {
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
mangle: true,
|
||||
preserveComments: 'some'
|
||||
},
|
||||
core: {
|
||||
src: '<%= concat.bootstrap.dest %>',
|
||||
dest: 'dist/js/<%= pkg.name %>.min.js'
|
||||
},
|
||||
customize: {
|
||||
src: configBridge.paths.customizerJs,
|
||||
dest: 'docs/assets/js/customize.min.js'
|
||||
},
|
||||
docsJs: {
|
||||
src: configBridge.paths.docsJs,
|
||||
dest: 'docs/assets/js/docs.min.js'
|
||||
}
|
||||
},
|
||||
|
||||
qunit: {
|
||||
options: {
|
||||
inject: 'js/tests/unit/phantom.js'
|
||||
},
|
||||
files: 'js/tests/index.html'
|
||||
},
|
||||
|
||||
less: {
|
||||
compileCore: {
|
||||
options: {
|
||||
strictMath: true,
|
||||
sourceMap: true,
|
||||
outputSourceFiles: true,
|
||||
sourceMapURL: '<%= pkg.name %>.css.map',
|
||||
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
|
||||
},
|
||||
src: 'less/bootstrap.less',
|
||||
dest: 'dist/css/<%= pkg.name %>.css'
|
||||
},
|
||||
compileTheme: {
|
||||
options: {
|
||||
strictMath: true,
|
||||
sourceMap: true,
|
||||
outputSourceFiles: true,
|
||||
sourceMapURL: '<%= pkg.name %>-theme.css.map',
|
||||
sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'
|
||||
},
|
||||
src: 'less/theme.less',
|
||||
dest: 'dist/css/<%= pkg.name %>-theme.css'
|
||||
}
|
||||
},
|
||||
|
||||
autoprefixer: {
|
||||
options: {
|
||||
browsers: configBridge.config.autoprefixerBrowsers
|
||||
},
|
||||
core: {
|
||||
options: {
|
||||
map: true
|
||||
},
|
||||
src: 'dist/css/<%= pkg.name %>.css'
|
||||
},
|
||||
theme: {
|
||||
options: {
|
||||
map: true
|
||||
},
|
||||
src: 'dist/css/<%= pkg.name %>-theme.css'
|
||||
},
|
||||
docs: {
|
||||
src: ['docs/assets/css/src/docs.css']
|
||||
},
|
||||
examples: {
|
||||
expand: true,
|
||||
cwd: 'docs/examples/',
|
||||
src: ['**/*.css'],
|
||||
dest: 'docs/examples/'
|
||||
}
|
||||
},
|
||||
|
||||
csslint: {
|
||||
options: {
|
||||
csslintrc: 'less/.csslintrc'
|
||||
},
|
||||
dist: [
|
||||
'dist/css/bootstrap.css',
|
||||
'dist/css/bootstrap-theme.css'
|
||||
],
|
||||
examples: [
|
||||
'docs/examples/**/*.css'
|
||||
],
|
||||
docs: {
|
||||
options: {
|
||||
ids: false,
|
||||
'overqualified-elements': false
|
||||
},
|
||||
src: 'docs/assets/css/src/docs.css'
|
||||
}
|
||||
},
|
||||
|
||||
cssmin: {
|
||||
options: {
|
||||
// TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
|
||||
// and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
|
||||
compatibility: 'ie8',
|
||||
keepSpecialComments: '*',
|
||||
sourceMap: true,
|
||||
advanced: false
|
||||
},
|
||||
minifyCore: {
|
||||
src: 'dist/css/<%= pkg.name %>.css',
|
||||
dest: 'dist/css/<%= pkg.name %>.min.css'
|
||||
},
|
||||
minifyTheme: {
|
||||
src: 'dist/css/<%= pkg.name %>-theme.css',
|
||||
dest: 'dist/css/<%= pkg.name %>-theme.min.css'
|
||||
},
|
||||
docs: {
|
||||
src: [
|
||||
'docs/assets/css/ie10-viewport-bug-workaround.css',
|
||||
'docs/assets/css/src/pygments-manni.css',
|
||||
'docs/assets/css/src/docs.css'
|
||||
],
|
||||
dest: 'docs/assets/css/docs.min.css'
|
||||
}
|
||||
},
|
||||
|
||||
csscomb: {
|
||||
options: {
|
||||
config: 'less/.csscomb.json'
|
||||
},
|
||||
dist: {
|
||||
expand: true,
|
||||
cwd: 'dist/css/',
|
||||
src: ['*.css', '!*.min.css'],
|
||||
dest: 'dist/css/'
|
||||
},
|
||||
examples: {
|
||||
expand: true,
|
||||
cwd: 'docs/examples/',
|
||||
src: '**/*.css',
|
||||
dest: 'docs/examples/'
|
||||
},
|
||||
docs: {
|
||||
src: 'docs/assets/css/src/docs.css',
|
||||
dest: 'docs/assets/css/src/docs.css'
|
||||
}
|
||||
},
|
||||
|
||||
copy: {
|
||||
fonts: {
|
||||
expand: true,
|
||||
src: 'fonts/*',
|
||||
dest: 'dist/'
|
||||
},
|
||||
docs: {
|
||||
expand: true,
|
||||
cwd: 'dist/',
|
||||
src: [
|
||||
'**/*'
|
||||
],
|
||||
dest: 'docs/dist/'
|
||||
}
|
||||
},
|
||||
|
||||
connect: {
|
||||
server: {
|
||||
options: {
|
||||
port: 3000,
|
||||
base: '.'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
jekyll: {
|
||||
options: {
|
||||
config: '_config.yml'
|
||||
},
|
||||
docs: {},
|
||||
github: {
|
||||
options: {
|
||||
raw: 'github: true'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
htmlmin: {
|
||||
dist: {
|
||||
options: {
|
||||
collapseWhitespace: true,
|
||||
conservativeCollapse: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true
|
||||
},
|
||||
expand: true,
|
||||
cwd: '_gh_pages',
|
||||
dest: '_gh_pages',
|
||||
src: [
|
||||
'**/*.html',
|
||||
'!examples/**/*.html'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
jade: {
|
||||
options: {
|
||||
pretty: true,
|
||||
data: getLessVarsData
|
||||
},
|
||||
customizerVars: {
|
||||
src: 'docs/_jade/customizer-variables.jade',
|
||||
dest: 'docs/_includes/customizer-variables.html'
|
||||
},
|
||||
customizerNav: {
|
||||
src: 'docs/_jade/customizer-nav.jade',
|
||||
dest: 'docs/_includes/nav/customize.html'
|
||||
}
|
||||
},
|
||||
|
||||
htmllint: {
|
||||
options: {
|
||||
ignore: [
|
||||
'Attribute "autocomplete" not allowed on element "button" at this point.',
|
||||
'Attribute "autocomplete" is only allowed when the input type is "color", "date", "datetime", "datetime-local", "email", "month", "number", "password", "range", "search", "tel", "text", "time", "url", or "week".',
|
||||
'Element "img" is missing required attribute "src".'
|
||||
]
|
||||
},
|
||||
src: '_gh_pages/**/*.html'
|
||||
},
|
||||
|
||||
watch: {
|
||||
src: {
|
||||
files: '<%= jshint.core.src %>',
|
||||
tasks: ['jshint:core', 'qunit', 'concat']
|
||||
},
|
||||
test: {
|
||||
files: '<%= jshint.test.src %>',
|
||||
tasks: ['jshint:test', 'qunit']
|
||||
},
|
||||
less: {
|
||||
files: 'less/**/*.less',
|
||||
tasks: 'less'
|
||||
}
|
||||
},
|
||||
|
||||
sed: {
|
||||
versionNumber: {
|
||||
pattern: (function () {
|
||||
var old = grunt.option('oldver');
|
||||
return old ? RegExp.quote(old) : old;
|
||||
})(),
|
||||
replacement: grunt.option('newver'),
|
||||
exclude: [
|
||||
'dist/fonts',
|
||||
'docs/assets',
|
||||
'fonts',
|
||||
'js/tests/vendor',
|
||||
'node_modules',
|
||||
'test-infra'
|
||||
],
|
||||
recursive: true
|
||||
}
|
||||
},
|
||||
|
||||
'saucelabs-qunit': {
|
||||
all: {
|
||||
options: {
|
||||
build: process.env.TRAVIS_JOB_ID,
|
||||
throttled: 10,
|
||||
maxRetries: 3,
|
||||
maxPollRetries: 4,
|
||||
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
|
||||
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
exec: {
|
||||
npmUpdate: {
|
||||
command: 'npm update'
|
||||
}
|
||||
},
|
||||
|
||||
compress: {
|
||||
main: {
|
||||
options: {
|
||||
archive: 'bootstrap-<%= pkg.version %>-dist.zip',
|
||||
mode: 'zip',
|
||||
level: 9,
|
||||
pretty: true
|
||||
},
|
||||
files: [
|
||||
{
|
||||
expand: true,
|
||||
cwd: 'dist/',
|
||||
src: ['**'],
|
||||
dest: 'bootstrap-<%= pkg.version %>-dist'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// These plugins provide necessary tasks.
|
||||
require('load-grunt-tasks')(grunt, { scope: 'devDependencies' });
|
||||
require('time-grunt')(grunt);
|
||||
|
||||
// Docs HTML validation task
|
||||
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
|
||||
|
||||
var runSubset = function (subset) {
|
||||
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
|
||||
};
|
||||
var isUndefOrNonZero = function (val) {
|
||||
return val === undefined || val !== '0';
|
||||
};
|
||||
|
||||
// Test task.
|
||||
var testSubtasks = [];
|
||||
// Skip core tests if running a different subset of the test suite
|
||||
if (runSubset('core') &&
|
||||
// Skip core tests if this is a Savage build
|
||||
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
|
||||
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']);
|
||||
}
|
||||
// Skip HTML validation if running a different subset of the test suite
|
||||
if (runSubset('validate-html') &&
|
||||
// Skip HTML5 validator on Travis when [skip validator] is in the commit message
|
||||
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
|
||||
testSubtasks.push('validate-html');
|
||||
}
|
||||
// Only run Sauce Labs tests if there's a Sauce access key
|
||||
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
|
||||
// Skip Sauce if running a different subset of the test suite
|
||||
runSubset('sauce-js-unit') &&
|
||||
// Skip Sauce on Travis when [skip sauce] is in the commit message
|
||||
isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
|
||||
testSubtasks.push('connect');
|
||||
testSubtasks.push('saucelabs-qunit');
|
||||
}
|
||||
grunt.registerTask('test', testSubtasks);
|
||||
grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
|
||||
|
||||
// JS distribution task.
|
||||
grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);
|
||||
|
||||
// CSS distribution task.
|
||||
grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);
|
||||
grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']);
|
||||
|
||||
// Full distribution task.
|
||||
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']);
|
||||
|
||||
// Version numbering task.
|
||||
// grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
|
||||
// This can be overzealous, so its changes should always be manually reviewed!
|
||||
grunt.registerTask('change-version-number', 'sed');
|
||||
|
||||
grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); });
|
||||
|
||||
// task for building customizer
|
||||
grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
|
||||
grunt.registerTask('build-customizer-html', 'jade');
|
||||
grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
|
||||
var banner = grunt.template.process('<%= banner %>');
|
||||
generateRawFiles(grunt, banner);
|
||||
});
|
||||
|
||||
grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
|
||||
var srcFiles = grunt.config.get('concat.bootstrap.src');
|
||||
var destFilepath = 'dist/js/npm.js';
|
||||
generateCommonJSModule(grunt, srcFiles, destFilepath);
|
||||
});
|
||||
|
||||
// Docs task.
|
||||
grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
|
||||
grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);
|
||||
grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);
|
||||
grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
|
||||
grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']);
|
||||
|
||||
grunt.registerTask('prep-release', ['dist', 'docs', 'jekyll:github', 'htmlmin', 'compress']);
|
||||
|
||||
// Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
|
||||
// This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
|
||||
grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
|
||||
grunt.registerTask('_update-shrinkwrap', function () {
|
||||
var done = this.async();
|
||||
npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) {
|
||||
if (err) {
|
||||
grunt.fail.warn(err);
|
||||
}
|
||||
var dest = 'test-infra/npm-shrinkwrap.json';
|
||||
fs.renameSync('npm-shrinkwrap.json', dest);
|
||||
grunt.log.writeln('File ' + dest.cyan + ' updated.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011-2015 Twitter, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,139 +0,0 @@
|
||||
# [Bootstrap](http://getbootstrap.com)
|
||||
|
||||
[](https://bootstrap-slack.herokuapp.com)
|
||||

|
||||
[](https://www.npmjs.com/package/bootstrap)
|
||||
[](https://travis-ci.org/twbs/bootstrap)
|
||||
[](https://david-dm.org/twbs/bootstrap#info=devDependencies)
|
||||
[](https://www.nuget.org/packages/Bootstrap)
|
||||
[](https://saucelabs.com/u/bootstrap)
|
||||
|
||||
Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thornton](https://twitter.com/fat), and maintained by the [core team](https://github.com/orgs/twbs/people) with the massive support and involvement of the community.
|
||||
|
||||
To get started, check out <http://getbootstrap.com>!
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Quick start](#quick-start)
|
||||
* [Bugs and feature requests](#bugs-and-feature-requests)
|
||||
* [Documentation](#documentation)
|
||||
* [Contributing](#contributing)
|
||||
* [Community](#community)
|
||||
* [Versioning](#versioning)
|
||||
* [Creators](#creators)
|
||||
* [Copyright and license](#copyright-and-license)
|
||||
|
||||
|
||||
## Quick start
|
||||
|
||||
Several quick start options are available:
|
||||
|
||||
* [Download the latest release](https://github.com/twbs/bootstrap/archive/v3.3.6.zip).
|
||||
* Clone the repo: `git clone https://github.com/twbs/bootstrap.git`.
|
||||
* Install with [Bower](http://bower.io): `bower install bootstrap`.
|
||||
* Install with [npm](https://www.npmjs.com): `npm install bootstrap`.
|
||||
* Install with [Meteor](https://www.meteor.com): `meteor add twbs:bootstrap`.
|
||||
* Install with [Composer](https://getcomposer.org): `composer require twbs/bootstrap`.
|
||||
|
||||
Read the [Getting started page](http://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more.
|
||||
|
||||
### What's included
|
||||
|
||||
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this:
|
||||
|
||||
```
|
||||
bootstrap/
|
||||
├── css/
|
||||
│ ├── bootstrap.css
|
||||
│ ├── bootstrap.css.map
|
||||
│ ├── bootstrap.min.css
|
||||
│ ├── bootstrap.min.css.map
|
||||
│ ├── bootstrap-theme.css
|
||||
│ ├── bootstrap-theme.css.map
|
||||
│ ├── bootstrap-theme.min.css
|
||||
│ └── bootstrap-theme.min.css.map
|
||||
├── js/
|
||||
│ ├── bootstrap.js
|
||||
│ └── bootstrap.min.js
|
||||
└── fonts/
|
||||
├── glyphicons-halflings-regular.eot
|
||||
├── glyphicons-halflings-regular.svg
|
||||
├── glyphicons-halflings-regular.ttf
|
||||
├── glyphicons-halflings-regular.woff
|
||||
└── glyphicons-halflings-regular.woff2
|
||||
```
|
||||
|
||||
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). CSS [source maps](https://developer.chrome.com/devtools/docs/css-preprocessors) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Fonts from Glyphicons are included, as is the optional Bootstrap theme.
|
||||
|
||||
|
||||
## Bugs and feature requests
|
||||
|
||||
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new).
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at <http://getbootstrap.com>. The docs may also be run locally.
|
||||
|
||||
### Running documentation locally
|
||||
|
||||
1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v3.0.x).
|
||||
**Note for Windows users:** Read [this unofficial guide](http://jekyll-windows.juthilo.com/) to get Jekyll up and running without problems.
|
||||
2. Install the Ruby-based syntax highlighter, [Rouge](https://github.com/jneen/rouge), with `gem install rouge`.
|
||||
3. From the root `/bootstrap` directory, run `jekyll serve` in the command line.
|
||||
4. Open `http://localhost:9001` in your browser, and voilà.
|
||||
|
||||
Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/).
|
||||
|
||||
### Documentation for previous releases
|
||||
|
||||
Documentation for v2.3.2 has been made available for the time being at <http://getbootstrap.com/2.3.2/> while folks transition to Bootstrap 3.
|
||||
|
||||
[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development.
|
||||
|
||||
Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/master/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo).
|
||||
|
||||
Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>.
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
Get updates on Bootstrap's development and chat with the project maintainers and community members.
|
||||
|
||||
* Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap).
|
||||
* Read and subscribe to [The Official Bootstrap Blog](http://blog.getbootstrap.com).
|
||||
* Join [the official Slack room](https://bootstrap-slack.herokuapp.com).
|
||||
* Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel.
|
||||
* Implementation help may be found at Stack Overflow (tagged [`twitter-bootstrap-3`](https://stackoverflow.com/questions/tagged/twitter-bootstrap-3)).
|
||||
* Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability.
|
||||
|
||||
|
||||
## Versioning
|
||||
|
||||
For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](http://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible.
|
||||
|
||||
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. Release announcement posts on [the official Bootstrap blog](http://blog.getbootstrap.com) contain summaries of the most noteworthy changes made in each release.
|
||||
|
||||
|
||||
## Creators
|
||||
|
||||
**Mark Otto**
|
||||
|
||||
* <https://twitter.com/mdo>
|
||||
* <https://github.com/mdo>
|
||||
|
||||
**Jacob Thornton**
|
||||
|
||||
* <https://twitter.com/fat>
|
||||
* <https://github.com/fat>
|
||||
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Code and documentation copyright 2011-2015 Twitter, Inc. Code released under [the MIT license](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE).
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "bootstrap",
|
||||
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
|
||||
"keywords": [
|
||||
"css",
|
||||
"js",
|
||||
"less",
|
||||
"mobile-first",
|
||||
"responsive",
|
||||
"front-end",
|
||||
"framework",
|
||||
"web"
|
||||
],
|
||||
"homepage": "http://getbootstrap.com",
|
||||
"license": "MIT",
|
||||
"moduleType": "globals",
|
||||
"main": [
|
||||
"less/bootstrap.less",
|
||||
"dist/js/bootstrap.js"
|
||||
],
|
||||
"ignore": [
|
||||
"/.*",
|
||||
"_config.yml",
|
||||
"CNAME",
|
||||
"composer.json",
|
||||
"CONTRIBUTING.md",
|
||||
"docs",
|
||||
"js/tests",
|
||||
"test-infra"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "1.9.1 - 2"
|
||||
}
|
||||
}
|
||||
@@ -1,587 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.6 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-primary.disabled,
|
||||
.btn-success.disabled,
|
||||
.btn-info.disabled,
|
||||
.btn-warning.disabled,
|
||||
.btn-danger.disabled,
|
||||
.btn-default[disabled],
|
||||
.btn-primary[disabled],
|
||||
.btn-success[disabled],
|
||||
.btn-info[disabled],
|
||||
.btn-warning[disabled],
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
fieldset[disabled] .btn-primary,
|
||||
fieldset[disabled] .btn-success,
|
||||
fieldset[disabled] .btn-info,
|
||||
fieldset[disabled] .btn-warning,
|
||||
fieldset[disabled] .btn-danger {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-default .badge,
|
||||
.btn-primary .badge,
|
||||
.btn-success .badge,
|
||||
.btn-info .badge,
|
||||
.btn-warning .badge,
|
||||
.btn-danger .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-default[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
.btn-default.disabled:hover,
|
||||
.btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default:hover,
|
||||
.btn-default.disabled:focus,
|
||||
.btn-default[disabled]:focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
.btn-default.disabled.focus,
|
||||
.btn-default[disabled].focus,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
.btn-default.disabled:active,
|
||||
.btn-default[disabled]:active,
|
||||
fieldset[disabled] .btn-default:active,
|
||||
.btn-default.disabled.active,
|
||||
.btn-default[disabled].active,
|
||||
fieldset[disabled] .btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #265a88;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #265a88;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled],
|
||||
fieldset[disabled] .btn-primary,
|
||||
.btn-primary.disabled:hover,
|
||||
.btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary:hover,
|
||||
.btn-primary.disabled:focus,
|
||||
.btn-primary[disabled]:focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
.btn-primary.disabled.focus,
|
||||
.btn-primary[disabled].focus,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
.btn-primary.disabled:active,
|
||||
.btn-primary[disabled]:active,
|
||||
fieldset[disabled] .btn-primary:active,
|
||||
.btn-primary.disabled.active,
|
||||
.btn-primary[disabled].active,
|
||||
fieldset[disabled] .btn-primary.active {
|
||||
background-color: #265a88;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success.disabled,
|
||||
.btn-success[disabled],
|
||||
fieldset[disabled] .btn-success,
|
||||
.btn-success.disabled:hover,
|
||||
.btn-success[disabled]:hover,
|
||||
fieldset[disabled] .btn-success:hover,
|
||||
.btn-success.disabled:focus,
|
||||
.btn-success[disabled]:focus,
|
||||
fieldset[disabled] .btn-success:focus,
|
||||
.btn-success.disabled.focus,
|
||||
.btn-success[disabled].focus,
|
||||
fieldset[disabled] .btn-success.focus,
|
||||
.btn-success.disabled:active,
|
||||
.btn-success[disabled]:active,
|
||||
fieldset[disabled] .btn-success:active,
|
||||
.btn-success.disabled.active,
|
||||
.btn-success[disabled].active,
|
||||
fieldset[disabled] .btn-success.active {
|
||||
background-color: #419641;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info.disabled,
|
||||
.btn-info[disabled],
|
||||
fieldset[disabled] .btn-info,
|
||||
.btn-info.disabled:hover,
|
||||
.btn-info[disabled]:hover,
|
||||
fieldset[disabled] .btn-info:hover,
|
||||
.btn-info.disabled:focus,
|
||||
.btn-info[disabled]:focus,
|
||||
fieldset[disabled] .btn-info:focus,
|
||||
.btn-info.disabled.focus,
|
||||
.btn-info[disabled].focus,
|
||||
fieldset[disabled] .btn-info.focus,
|
||||
.btn-info.disabled:active,
|
||||
.btn-info[disabled]:active,
|
||||
fieldset[disabled] .btn-info:active,
|
||||
.btn-info.disabled.active,
|
||||
.btn-info[disabled].active,
|
||||
fieldset[disabled] .btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning.disabled,
|
||||
.btn-warning[disabled],
|
||||
fieldset[disabled] .btn-warning,
|
||||
.btn-warning.disabled:hover,
|
||||
.btn-warning[disabled]:hover,
|
||||
fieldset[disabled] .btn-warning:hover,
|
||||
.btn-warning.disabled:focus,
|
||||
.btn-warning[disabled]:focus,
|
||||
fieldset[disabled] .btn-warning:focus,
|
||||
.btn-warning.disabled.focus,
|
||||
.btn-warning[disabled].focus,
|
||||
fieldset[disabled] .btn-warning.focus,
|
||||
.btn-warning.disabled:active,
|
||||
.btn-warning[disabled]:active,
|
||||
fieldset[disabled] .btn-warning:active,
|
||||
.btn-warning.disabled.active,
|
||||
.btn-warning[disabled].active,
|
||||
fieldset[disabled] .btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger.disabled,
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-danger,
|
||||
.btn-danger.disabled:hover,
|
||||
.btn-danger[disabled]:hover,
|
||||
fieldset[disabled] .btn-danger:hover,
|
||||
.btn-danger.disabled:focus,
|
||||
.btn-danger[disabled]:focus,
|
||||
fieldset[disabled] .btn-danger:focus,
|
||||
.btn-danger.disabled.focus,
|
||||
.btn-danger[disabled].focus,
|
||||
fieldset[disabled] .btn-danger.focus,
|
||||
.btn-danger.disabled:active,
|
||||
.btn-danger[disabled]:active,
|
||||
fieldset[disabled] .btn-danger:active,
|
||||
.btn-danger.disabled.active,
|
||||
.btn-danger[disabled].active,
|
||||
fieldset[disabled] .btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
background-image: none;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e8e8e8;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #2e6da4;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .open > a,
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .open > a,
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-striped {
|
||||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #286090;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.list-group-item.active .badge,
|
||||
.list-group-item.active:hover .badge,
|
||||
.list-group-item.active:focus .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-theme.css.map */
|
||||