mirror of
				https://github.com/arcan1s/ffxivbis.git
				synced 2025-10-30 21:23:41 +00:00 
			
		
		
		
	postgres demo support
This commit is contained in:
		
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @ -109,7 +109,7 @@ Service which allows to manage savage loot distribution easy. | ||||
|  | ||||
|     * `include`: path to include configuration directory, string, optional. | ||||
|     * `logging`: path to logging configuration, see `logging.ini` for reference, string, optional. | ||||
|     * `database`: database provide name, string, required. Allowed values: `sqlite`. | ||||
|     * `database`: database provide name, string, required. Allowed values: `sqlite`, `postgres`. | ||||
|     * `priority`: methods of `Player` class which will be called to sort players for loot priority, space separated list of strings, required. | ||||
|      | ||||
| * `ariyala` section | ||||
| @ -129,6 +129,17 @@ Service which allows to manage savage loot distribution easy. | ||||
|     * `root_username`: username of administrator, string, required. | ||||
|     * `root_password`: md5 hashed password of administrator, string, required. | ||||
|      | ||||
| * `postgres` section | ||||
|  | ||||
|     Database settings for `postgres` provider. | ||||
|      | ||||
|     * `database`: database name, string, required. | ||||
|     * `host`: database host, string, required. | ||||
|     * `password`: database password, string, required. | ||||
|     * `port`: database port, int, required. | ||||
|     * `username`: database username, string, required. | ||||
|     * `migrations_path`: path to database migrations, string, required. | ||||
|      | ||||
| * `sqlite` section | ||||
|  | ||||
|     Database settings for `sqlite` provider. | ||||
|  | ||||
							
								
								
									
										5
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								TODO.md
									
									
									
									
									
								
							| @ -1,2 +1,3 @@ | ||||
| [ ] postgres support | ||||
| [ ] pretty UI | ||||
| * [ ] items improvements | ||||
| * [ ] multiple parties support | ||||
| * [ ] pretty UI | ||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							| @ -28,7 +28,6 @@ setup( | ||||
|         'aiohttp', | ||||
|         'aiohttp_jinja2', | ||||
|         'aiohttp_security', | ||||
|         'aiosqlite', | ||||
|         'Jinja2', | ||||
|         'passlib', | ||||
|         'requests', | ||||
| @ -44,6 +43,8 @@ setup( | ||||
|     include_package_data=True, | ||||
|  | ||||
|     extras_require={ | ||||
|         'Postgresql': ['aiopg'], | ||||
|         'SQLite':  ['aiosqlite'], | ||||
|         'test': ['coverage', 'pytest'], | ||||
|     }, | ||||
| ) | ||||
|  | ||||
| @ -26,7 +26,7 @@ class Application: | ||||
|     def run(self) -> None: | ||||
|         loop = asyncio.get_event_loop() | ||||
|  | ||||
|         database = Database.get(self.config) | ||||
|         database = loop.run_until_complete(Database.get(self.config)) | ||||
|         database.migration() | ||||
|  | ||||
|         party = loop.run_until_complete(Party.get(database)) | ||||
|  | ||||
| @ -35,22 +35,30 @@ class Database: | ||||
|         return int(datetime.datetime.now().timestamp()) | ||||
|  | ||||
|     @classmethod | ||||
|     def get(cls: Type[Database], config: Configuration) -> Database: | ||||
|     async def get(cls: Type[Database], config: Configuration) -> Database: | ||||
|         database_type = config.get('settings', 'database') | ||||
|         database_settings = config.get_section(database_type) | ||||
|  | ||||
|         if database_type == 'sqlite': | ||||
|             from .sqlite import SQLiteDatabase | ||||
|             obj: Type[Database] = SQLiteDatabase | ||||
|         elif database_type == 'postgres': | ||||
|             from .postgres import PostgresDatabase | ||||
|             obj = PostgresDatabase | ||||
|         else: | ||||
|             raise InvalidDatabase(database_type) | ||||
|  | ||||
|         return obj(**database_settings) | ||||
|         database = obj(**database_settings) | ||||
|         await database.init() | ||||
|         return database | ||||
|  | ||||
|     @property | ||||
|     def connection(self) -> str: | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     async def init(self) -> None: | ||||
|         pass | ||||
|  | ||||
|     async def delete_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None: | ||||
|         raise NotImplementedError | ||||
|  | ||||
|  | ||||
							
								
								
									
										166
									
								
								src/service/core/postgres.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/service/core/postgres.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,166 @@ | ||||
| # | ||||
| # Copyright (c) 2019 Evgeniy Alekseev. | ||||
| # | ||||
| # This file is part of ffxivbis | ||||
| # (see https://github.com/arcan1s/ffxivbis). | ||||
| # | ||||
| # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause | ||||
| # | ||||
| import asyncpg | ||||
|  | ||||
| from passlib.hash import md5_crypt | ||||
| from psycopg2.extras import DictCursor | ||||
| from typing import List, Optional, Union | ||||
|  | ||||
| from service.models.bis import BiS | ||||
| from service.models.job import Job | ||||
| from service.models.loot import Loot | ||||
| from service.models.piece import Piece | ||||
| from service.models.player import Player, PlayerId | ||||
| from service.models.upgrade import Upgrade | ||||
| from service.models.user import User | ||||
|  | ||||
| from .database import Database | ||||
|  | ||||
|  | ||||
| class PostgresDatabase(Database): | ||||
|  | ||||
|     def __init__(self, host: str, port: int, username: str, password: str, database: str, migrations_path: str) -> None: | ||||
|         Database.__init__(self, migrations_path) | ||||
|         self.host = host | ||||
|         self.port = int(port) | ||||
|         self.username = username | ||||
|         self.password = password | ||||
|         self.database = database | ||||
|         self.pool: asyncpg.pool.Pool = None  # type: ignore | ||||
|  | ||||
|     @property | ||||
|     def connection(self) -> str: | ||||
|         return 'postgresql://{username}:{password}@{host}:{port}/{database}'.format( | ||||
|             username=self.username, password=self.password, host=self.host, port=self.port, database=self.database | ||||
|         ) | ||||
|  | ||||
|     async def init(self) -> None: | ||||
|         self.pool = await asyncpg.create_pool(host=self.host, port=self.port, username=self.username, | ||||
|                                               password=self.password, database=self.database) | ||||
|  | ||||
|     async def delete_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None: | ||||
|         player = await self.get_player(player_id) | ||||
|         if player is None: | ||||
|             return | ||||
|  | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute( | ||||
|                 '''delete from loot | ||||
|                 where loot_id in ( | ||||
|                     select loot_id from loot  | ||||
|                     where player_id = $1 and piece = $2 and is_tome = $3 order by created desc limit 1 | ||||
|                 )''', | ||||
|                 player, piece.name, getattr(piece, 'is_tome', True) | ||||
|             ) | ||||
|  | ||||
|     async def delete_piece_bis(self, player_id: PlayerId, piece: Piece) -> None: | ||||
|         player = await self.get_player(player_id) | ||||
|         if player is None: | ||||
|             return | ||||
|  | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute( | ||||
|                 '''delete from bis where player_id = $1 and piece = $2''', | ||||
|                 player, piece.name) | ||||
|  | ||||
|     async def delete_player(self, player_id: PlayerId) -> None: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute('''delete from players where nick = $1 and job = $2''', | ||||
|                                player_id.nick, player_id.job.name) | ||||
|  | ||||
|     async def delete_user(self, username: str) -> None: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute('''delete from users where username = $1''', username) | ||||
|  | ||||
|     async def get_party(self) -> List[Player]: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             rows = await conn.fetch('''select * from bis''') | ||||
|             bis_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows] | ||||
|  | ||||
|             rows = await conn.fetch('''select * from loot''') | ||||
|             loot_pieces = [Loot(row['player_id'], Piece.get(row)) for row in rows] | ||||
|  | ||||
|             rows = await conn.fetch('''select * from players''') | ||||
|             party = { | ||||
|                 row['player_id']: Player(Job[row['job']], row['nick'], BiS(), [], row['bis_link'], row['priority']) | ||||
|                 for row in rows | ||||
|             } | ||||
|  | ||||
|         return self.set_loot(party, bis_pieces, loot_pieces) | ||||
|  | ||||
|     async def get_player(self, player_id: PlayerId) -> Optional[int]: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             player = await conn.fetchrow('''select player_id from players where nick = $1 and job = $2''', | ||||
|                                          player_id.nick, player_id.job.name) | ||||
|             return player['player_id'] if player is not None else None | ||||
|  | ||||
|     async def get_user(self, username: str) -> Optional[User]: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             user = await conn.fetchrow('''select * from users where username = $1''', username) | ||||
|             return User(user['username'], user['password'], user['permission']) if user is not None else None | ||||
|  | ||||
|     async def get_users(self) -> List[User]: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             users = await conn.fetch('''select * from users''') | ||||
|             return [User(user['username'], user['password'], user['permission']) for user in users] | ||||
|  | ||||
|     async def insert_piece(self, player_id: PlayerId, piece: Union[Piece, Upgrade]) -> None: | ||||
|         player = await self.get_player(player_id) | ||||
|         if player is None: | ||||
|             return | ||||
|  | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute( | ||||
|                 '''insert into loot | ||||
|                 (created, piece, is_tome, player_id) | ||||
|                 values | ||||
|                 ($1, $2, $3, $4)''', | ||||
|                 Database.now(), piece.name, getattr(piece, 'is_tome', True), player | ||||
|             ) | ||||
|  | ||||
|     async def insert_piece_bis(self, player_id: PlayerId, piece: Piece) -> None: | ||||
|         player = await self.get_player(player_id) | ||||
|         if player is None: | ||||
|             return | ||||
|  | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute( | ||||
|                 '''insert into bis | ||||
|                 (created, piece, is_tome, player_id) | ||||
|                 values | ||||
|                 ($1, $2, $3, $4) | ||||
|                 on conflict on constraint bis_piece_player_id_idx do update set | ||||
|                 created = $1, is_tome = $3''', | ||||
|                 Database.now(), piece.name, piece.is_tome, player | ||||
|             ) | ||||
|  | ||||
|     async def insert_player(self, player: Player) -> None: | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute( | ||||
|                 '''insert into players | ||||
|                 (created, nick, job, bis_link, priority) | ||||
|                 values | ||||
|                 ($1, $2, $3, $4, $5) | ||||
|                 on conflict on constraint players_nick_job_idx do update set | ||||
|                 created = $1, bis_link = $4, priority = $5''', | ||||
|                 Database.now(), player.nick, player.job.name, player.link, player.priority | ||||
|             ) | ||||
|  | ||||
|     async def insert_user(self, user: User, hashed_password: bool) -> None: | ||||
|         password = user.password if hashed_password else md5_crypt.hash(user.password) | ||||
|         async with self.pool.acquire() as conn: | ||||
|             await conn.execute( | ||||
|                 '''insert into users | ||||
|                 (username, password, permission) | ||||
|                 values | ||||
|                 ($1, $2, $3) | ||||
|                 on conflict on constraint users_username_idx do update set | ||||
|                 password = $2, permission = $3''', | ||||
|                 user.username, password, user.permission | ||||
|             ) | ||||
		Reference in New Issue
	
	Block a user